{ "openapi": "3.1.0", "info": { "title": "AlgaPSA API", "version": "0.1.0", "description": "OpenAPI specification generated from registered route metadata." }, "servers": [ { "url": "https://algapsa.com", "description": "Production" }, { "url": "http://localhost:3000", "description": "Local development" } ], "tags": [ { "name": "AI" }, { "name": "Access Control & Users v1" }, { "name": "Accounting Exports" }, { "name": "Admin" }, { "name": "Assets" }, { "name": "Auth" }, { "name": "Automation" }, { "name": "Billing" }, { "name": "Billing Analytics" }, { "name": "Boards" }, { "name": "Categories" }, { "name": "Chat" }, { "name": "Client Contract Lines" }, { "name": "Clients" }, { "name": "Comments" }, { "name": "Contacts" }, { "name": "Contract Lines" }, { "name": "Documents" }, { "name": "Email" }, { "name": "Extension Gateway" }, { "name": "Extension Installs" }, { "name": "Extensions" }, { "name": "Files" }, { "name": "Financial" }, { "name": "Inbound Webhooks" }, { "name": "Integrations - QuickBooks" }, { "name": "Invoices" }, { "name": "Knowledge Base" }, { "name": "Meta & Utility v1" }, { "name": "Priorities" }, { "name": "Products" }, { "name": "Project Templates" }, { "name": "Projects" }, { "name": "QuickBooks v1" }, { "name": "Quotes & Contracts v1" }, { "name": "Search" }, { "name": "Service Catalog" }, { "name": "Service Categories" }, { "name": "Service Types" }, { "name": "Services" }, { "name": "Software" }, { "name": "SoftwareOne Extensions" }, { "name": "Statuses" }, { "name": "Storage" }, { "name": "System" }, { "name": "Telemetry" }, { "name": "Ticket Categories" }, { "name": "Webhooks" }, { "name": "Work Management v1" }, { "name": "Workflow Definitions" }, { "name": "Workflow Registry" }, { "name": "Workflow Runs" }, { "name": "Workflows v1" }, { "name": "accounting" }, { "name": "auth" }, { "name": "billing" }, { "name": "calendar" }, { "name": "chat" }, { "name": "client-portal" }, { "name": "clients" }, { "name": "documents" }, { "name": "email" }, { "name": "ext" }, { "name": "ext-bundles" }, { "name": "ext-debug" }, { "name": "ext-proxy" }, { "name": "import" }, { "name": "inbound" }, { "name": "integrations" }, { "name": "internal" }, { "name": "mobile" }, { "name": "online-meetings" }, { "name": "platform-feature-flags" }, { "name": "platform-notifications" }, { "name": "platform-reports" }, { "name": "public" }, { "name": "secrets" }, { "name": "share" }, { "name": "storage" }, { "name": "teams" }, { "name": "tenant-management" }, { "name": "tickets" }, { "name": "webhooks" } ], "components": { "securitySchemes": { "ApiKeyAuth": { "type": "apiKey", "in": "header", "name": "x-api-key" }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "Auth.js JWE session token" }, "SessionCookieAuth": { "type": "apiKey", "in": "cookie", "name": "authjs.session-token", "description": "Auth.js session cookie. In secure production deployments the cookie may be named __Secure-authjs.session-token; in local development it may include a port suffix." }, "WebhookSignatureAuth": { "type": "apiKey", "in": "header", "name": "x-webhook-signature", "description": "HMAC-SHA256 signature generated with ALGA_WEBHOOK_SECRET. POST signs the raw JSON body. GET signs the string GET:." }, "GooglePubSubJWT": { "type": "http", "scheme": "bearer", "bearerFormat": "Google Pub/Sub JWT", "description": "Google-signed JWT from Pub/Sub push. The webhook validates the audience against the webhook URL and checks the issuer/service account against tenant Google configuration." } }, "schemas": { "ErrorResponse": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] }, "PlaceholderObject": { "type": "object", "properties": {} }, "OAuthCallbackHtmlResponse": { "type": "string", "description": "HTML popup callback page. The page posts a JSON oauth-callback message to window.opener or window.parent via postMessage, then attempts to close itself." }, "OAuthCallbackQuery": { "type": "object", "properties": { "code": { "type": "string", "description": "OAuth 2.0 authorization code. Required unless the provider returned error." }, "state": { "type": "string", "description": "Base64-encoded JSON state object generated when the OAuth flow was initiated. It carries tenant context, providerId, redirectUri, timestamp, and nonce values used to complete the callback." }, "error": { "type": "string", "description": "OAuth error code returned by the provider, such as access_denied." }, "error_description": { "type": "string", "description": "Human-readable OAuth error description returned by the provider." } } }, "AuthSessionUser": { "type": "object", "properties": { "id": { "type": "string", "description": "User identifier from the users.user_id record." }, "email": { "type": "string", "format": "email", "description": "User's email address." }, "name": { "type": "string", "description": "User's display name, usually first_name plus last_name." }, "username": { "type": "string", "description": "User's login username." }, "image": { "type": "string", "description": "Avatar or profile image URL." }, "proToken": { "type": "string", "description": "Legacy Pro token value when present." }, "tenant": { "type": "string", "description": "Tenant UUID from the user session JWT." }, "tenantSlug": { "type": "string", "description": "URL-safe tenant slug for portal routing." }, "user_type": { "type": "string", "enum": [ "client", "internal" ], "description": "User classification from users.user_type." }, "clientId": { "type": "string", "format": "uuid", "description": "Client UUID from contacts.client_id for client-portal users." }, "contactId": { "type": "string", "format": "uuid", "description": "Contact UUID from users.contact_id for client-portal users." }, "plan": { "type": "string", "description": "Current tenant billing plan key." }, "addons": { "type": "array", "items": { "type": "string" }, "description": "Enabled tenant add-on keys." }, "trial_end": { "type": [ "string", "null" ], "description": "Trial expiry timestamp when present." }, "subscription_status": { "type": [ "string", "null" ], "enum": [ "active", "trialing", "past_due", "unpaid" ], "description": "Billing subscription status copied into the session token." }, "solo_pro_trial_end": { "type": [ "string", "null" ], "description": "Solo Pro trial expiry timestamp when present." }, "premium_trial_end": { "type": [ "string", "null" ], "description": "Premium trial expiry timestamp when present." }, "premium_trial_confirmed": { "type": "boolean", "description": "Whether the user confirmed the Premium trial." }, "premium_trial_effective_date": { "type": [ "string", "null" ], "description": "Premium trial effective date when present." } }, "required": [ "id", "email", "name", "username" ] }, "AuthSessionAuthenticatedResponse": { "type": "object", "properties": { "session_id": { "type": "string", "format": "uuid", "description": "Current session UUID from the sessions table, created at sign-in." }, "login_method": { "type": "string", "description": "Authentication method used for this session, such as credentials, google, or azure-ad." }, "user": { "$ref": "#/components/schemas/AuthSessionUser" } }, "required": [ "user" ] }, "EmptyObjectResponse": { "type": "object", "properties": {}, "additionalProperties": false, "description": "Empty object returned when no authenticated session is present." }, "AuthSessionResponse": { "anyOf": [ { "$ref": "#/components/schemas/AuthSessionAuthenticatedResponse" }, { "$ref": "#/components/schemas/EmptyObjectResponse" } ], "description": "Authenticated session data, or an empty object when the request has no valid session cookie." }, "FlatErrorResponse": { "type": "object", "properties": { "error": { "type": "string", "description": "Human-readable error message." } }, "required": [ "error" ] }, "ValidateApiKeyHeaders": { "type": "object", "properties": { "x-api-key": { "type": "string", "minLength": 1, "description": "Plaintext API key to validate. The service hashes this value before looking up the api_keys record." } }, "required": [ "x-api-key" ] }, "ValidateApiKeyResponse": { "type": "object", "properties": { "isValid": { "type": "boolean", "enum": [ true ], "description": "Indicates that the API key is active and valid." }, "userId": { "type": "string", "format": "uuid", "description": "UUID of the user who owns the API key, from api_keys.user_id." }, "tenant": { "type": "string", "description": "Tenant identifier scoped to this API key, from api_keys.tenant." } }, "required": [ "isValid", "userId", "tenant" ] }, "ValidateTokenHeaders": { "type": "object", "properties": { "authorization": { "type": "string", "description": "Optional Bearer token fallback. The route also accepts the Auth.js session token cookie." } } }, "ValidateTokenSuccessResponse": { "type": "object", "properties": { "isValid": { "type": "boolean", "enum": [ true ], "description": "Indicates that the request contains a valid Auth.js session token." }, "userType": { "type": "string", "enum": [ "internal", "client" ], "description": "User classification from users.user_type in the session JWT." }, "tenant": { "type": "string", "description": "Tenant identifier from the session JWT." } }, "required": [ "isValid", "userType", "tenant" ] }, "ValidateTokenUnauthorizedResponse": { "type": "object", "properties": { "isValid": { "type": "boolean", "enum": [ false ], "description": "No valid session token was found." } }, "required": [ "isValid" ] }, "NextAuthCatchAllParams": { "type": "object", "properties": { "nextauth": { "type": "string", "description": "NextAuth catch-all action path. Common values include csrf, providers, signin, signout, session, error, verify-request, webauthn-options, callback/credentials, callback/google, callback/azure-ad, and callback/keycloak." } }, "required": [ "nextauth" ] }, "NextAuthGetQuery": { "type": "object", "properties": { "callbackUrl": { "type": "string", "description": "Optional URL to redirect to after sign-in or sign-out flows." }, "error": { "type": "string", "description": "Optional NextAuth/OAuth error code shown by sign-in or error pages." }, "code": { "type": "string", "description": "OAuth authorization code for provider callback sub-routes." }, "state": { "type": "string", "description": "OAuth state value for provider callback sub-routes." }, "error_description": { "type": "string", "description": "Human-readable provider error description for OAuth callback sub-routes." } } }, "CsrfTokenResponse": { "type": "object", "properties": { "csrfToken": { "type": "string", "description": "Opaque double-submit CSRF token required for mutating NextAuth POST actions." } }, "required": [ "csrfToken" ] }, "ProviderInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Provider identifier such as credentials, google, azure-ad, or keycloak." }, "name": { "type": "string", "description": "Provider display name." }, "type": { "type": "string", "enum": [ "oauth", "oidc", "credentials", "email", "webauthn" ], "description": "NextAuth provider type." }, "signinUrl": { "type": "string", "description": "URL used to initiate sign-in with this provider." }, "callbackUrl": { "type": "string", "description": "URL the provider redirects to after authentication." } }, "required": [ "id", "name", "type", "signinUrl", "callbackUrl" ] }, "AuthProvidersResponse": { "type": "object", "additionalProperties": { "$ref": "#/components/schemas/ProviderInfo" }, "description": "Map of configured providers keyed by provider ID." }, "NextAuthGetResponse": { "anyOf": [ { "$ref": "#/components/schemas/AuthSessionAuthenticatedResponse" }, { "$ref": "#/components/schemas/EmptyObjectResponse" }, { "$ref": "#/components/schemas/CsrfTokenResponse" }, { "$ref": "#/components/schemas/AuthProvidersResponse" }, { "$ref": "#/components/schemas/OAuthCallbackHtmlResponse" }, { "$ref": "#/components/schemas/EmptyObjectResponse" } ], "description": "Representative successful GET response for the NextAuth catch-all route. The exact payload depends on the nextauth action: session returns a session object or {}, csrf returns csrfToken, providers returns a provider map, and page actions return HTML or redirects." }, "NextAuthPostBody": { "type": "object", "properties": { "email": { "type": "string", "format": "email", "description": "Credentials-provider email address for callback/credentials." }, "password": { "type": "string", "description": "Credentials-provider plaintext password for callback/credentials." }, "twoFactorCode": { "type": "string", "description": "TOTP code required when two-factor authentication is enabled and the device is not trusted." }, "userType": { "type": "string", "enum": [ "client", "internal" ], "description": "Optional user type used to scope credentials-provider lookup." }, "tenant": { "type": "string", "description": "Optional tenant slug used to resolve the tenant for credentials-provider login." }, "csrfToken": { "type": "string", "description": "CSRF token from GET /api/auth/csrf. Required for sign-in, sign-out, and other mutating NextAuth actions." }, "callbackUrl": { "type": "string", "description": "Post-authentication redirect URL." }, "redirect": { "type": "string", "description": "NextAuth redirect mode flag." }, "json": { "type": "string", "description": "NextAuth JSON response mode flag." }, "code": { "type": "string", "description": "OAuth authorization code for provider callback actions." }, "state": { "type": "string", "description": "OAuth state value for provider callback actions." }, "error": { "type": "string", "description": "Provider OAuth error code for callback actions." }, "error_description": { "type": "string", "description": "Provider OAuth error description for callback actions." } } }, "RedirectResponse": { "type": "string", "description": "HTTP redirect response. NextAuth sets or clears cookies depending on the action." }, "TelemetrySettingsGetResponse": { "type": "object", "properties": { "usageStatsEnabled": { "type": "boolean", "description": "Whether usage statistics are enabled by the ALGA_USAGE_STATS environment variable." }, "environmentVariable": { "type": "string", "enum": [ "ALGA_USAGE_STATS" ], "description": "Environment variable controlling telemetry collection." }, "currentValue": { "type": "string", "description": "Raw ALGA_USAGE_STATS environment value, or not set when absent." }, "controlledBy": { "type": "string", "enum": [ "environment" ], "description": "Telemetry is controlled by process environment, not runtime API writes." }, "message": { "type": "string", "description": "Human-readable explanation of the current telemetry setting." } }, "required": [ "usageStatsEnabled", "environmentVariable", "currentValue", "controlledBy", "message" ] }, "TelemetrySettingsPostResponse": { "type": "object", "properties": { "usageStatsEnabled": { "type": "boolean", "description": "Whether usage statistics are enabled by the ALGA_USAGE_STATS environment variable." }, "controlledBy": { "type": "string", "enum": [ "environment" ], "description": "Telemetry is controlled by process environment, not runtime API writes." }, "message": { "type": "string", "description": "Explains that telemetry settings cannot be changed through this API." } }, "required": [ "usageStatsEnabled", "controlledBy", "message" ] }, "AdminTelemetryErrorResponse": { "type": "object", "properties": { "error": { "type": "string", "description": "Error message such as Authentication required, Insufficient permissions, or the handler failure message." } }, "required": [ "error" ] }, "AccessUserIdParamV1": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." } }, "required": [ "id" ] }, "AccessRoleIdParamV1": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Role UUID from roles.role_id." } }, "required": [ "id" ] }, "AccessTeamIdParamV1": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." } }, "required": [ "id" ] }, "AccessTeamMemberParamsV1": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "userId": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." } }, "required": [ "id", "userId" ] }, "AccessTeamPermissionParamsV1": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "permissionId": { "type": "string", "format": "uuid", "description": "Permission UUID from permissions.permission_id." } }, "required": [ "id", "permissionId" ] }, "AccessUserTeamParamsV1": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "teamId": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." } }, "required": [ "id", "teamId" ] }, "AccessPermissionIdParamV1": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Permission UUID from permissions.permission_id." } }, "required": [ "id" ] }, "AccessUserListQueryV1": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] }, "username": { "type": "string" }, "first_name": { "type": "string" }, "last_name": { "type": "string" }, "email": { "type": "string" }, "user_type": { "type": "string", "enum": [ "internal", "client", "admin", "contractor" ] }, "role_id": { "type": "string", "format": "uuid" }, "team_id": { "type": "string", "format": "uuid" }, "is_inactive": { "type": "string", "enum": [ "true", "false" ] }, "include_permissions": { "type": "string", "enum": [ "true", "false" ] }, "include_teams": { "type": "string", "enum": [ "true", "false" ] }, "fields": { "type": "string" } } }, "AccessUserSearchQueryV1": { "type": "object", "properties": { "query": { "type": "string" }, "fields": { "type": "string", "description": "Controller query parser sends strings; schema expects an array of field names." }, "user_type": { "type": "string", "enum": [ "internal", "client", "admin", "contractor" ] }, "role_id": { "type": "string", "format": "uuid" }, "team_id": { "type": "string", "format": "uuid" }, "include_inactive": { "type": "string", "enum": [ "true", "false" ] }, "limit": { "type": "string" } }, "required": [ "query" ] }, "AccessUserActivityQueryV1": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "from_date": { "type": "string" }, "to_date": { "type": "string" }, "activity_type": { "type": "string", "description": "Controller converts single query value to one-element array for service filter." }, "ip_address": { "type": "string" } } }, "AccessUserRolesListQueryV1": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "search": { "type": "string" }, "is_inactive": { "type": "string", "enum": [ "true", "false" ] }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] } } }, "AccessRoleListQueryV1": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] }, "role_name": { "type": "string" }, "has_permissions": { "type": "string", "enum": [ "true", "false" ] }, "permission_resource": { "type": "string" }, "permission_action": { "type": "string" }, "is_template": { "type": "string", "enum": [ "true", "false" ] } } }, "AccessPermissionListQueryV1": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] }, "resource": { "type": "string" }, "action": { "type": "string" } } }, "AccessTeamListQueryV1": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] }, "team_name": { "type": "string" }, "manager_id": { "type": "string", "format": "uuid" }, "fields": { "type": "string" } } }, "AccessTeamSearchQueryV1": { "type": "object", "properties": { "query": { "type": "string" }, "page": { "type": "string" }, "limit": { "type": "string" }, "manager_id": { "type": "string", "format": "uuid" }, "has_manager": { "type": "string", "enum": [ "true", "false" ] }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] } } }, "AccessTeamAnalyticsQueryV1": { "type": "object", "properties": { "start_date": { "type": "string" }, "end_date": { "type": "string" }, "include_metrics": { "type": "string", "description": "Controller query parser provides strings; schema expects metric array." }, "granularity": { "type": "string", "enum": [ "daily", "weekly", "monthly" ] } } }, "AccessCreateUserBodyV1": { "type": "object", "properties": { "username": { "type": "string", "minLength": 3 }, "email": { "type": "string", "format": "email" }, "password": { "type": "string", "minLength": 8 }, "first_name": { "type": "string" }, "last_name": { "type": "string" }, "phone": { "type": "string" }, "timezone": { "type": "string" }, "user_type": { "type": "string", "enum": [ "internal", "client", "admin", "contractor" ] }, "contact_id": { "type": "string", "format": "uuid" }, "two_factor_enabled": { "type": "boolean" }, "is_google_user": { "type": "boolean" }, "is_inactive": { "type": "boolean" }, "role_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } } }, "required": [ "username", "email", "password" ] }, "AccessUpdateUserBodyV1": { "type": "object", "properties": { "username": { "type": "string", "minLength": 3 }, "email": { "type": "string", "format": "email" }, "first_name": { "type": "string" }, "last_name": { "type": "string" }, "phone": { "type": "string" }, "timezone": { "type": "string" }, "user_type": { "type": "string", "enum": [ "internal", "client", "admin", "contractor" ] }, "contact_id": { "type": "string", "format": "uuid" }, "two_factor_enabled": { "type": "boolean" }, "is_google_user": { "type": "boolean" }, "is_inactive": { "type": "boolean" }, "role_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "AccessChangePasswordBodyV1": { "type": "object", "properties": { "current_password": { "type": "string" }, "new_password": { "type": "string", "minLength": 8 }, "confirm_password": { "type": "string" } }, "required": [ "new_password", "confirm_password" ] }, "AccessUserRoleIdsBodyV1": { "type": "object", "properties": { "role_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1 } }, "required": [ "role_ids" ] }, "AccessUserPreferenceBodyV1": { "type": "object", "additionalProperties": {}, "description": "Controller forwards request body directly to UserService.updateUserPreferences()." }, "AccessEnable2FABodyV1": { "type": "object", "properties": { "secret": { "type": "string" }, "token": { "type": "string" } }, "required": [ "secret", "token" ] }, "AccessCreateRoleBodyV1": { "type": "object", "properties": { "role_name": { "type": "string" }, "description": { "type": "string" }, "permissions": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "copy_from_role_id": { "type": "string", "format": "uuid" }, "is_template": { "type": "boolean" } }, "required": [ "role_name" ] }, "AccessUpdateRoleBodyV1": { "type": "object", "properties": { "role_name": { "type": "string" }, "description": { "type": "string" }, "permissions": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "is_template": { "type": "boolean" } } }, "AccessRoleCloneBodyV1": { "type": "object", "properties": { "new_role_name": { "type": "string" }, "new_description": { "type": "string" }, "copy_permissions": { "type": "boolean" }, "copy_user_assignments": { "type": "boolean" } }, "required": [ "new_role_name" ] }, "AccessRolePermissionsBodyV1": { "type": "object", "properties": { "permission_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1 } }, "required": [ "permission_ids" ] }, "AccessBulkRolesBodyV1": { "type": "object", "properties": { "roles": { "type": "array", "items": { "$ref": "#/components/schemas/AccessCreateRoleBodyV1" }, "minItems": 1 } }, "required": [ "roles" ] }, "AccessCreatePermissionBodyV1": { "type": "object", "properties": { "resource": { "type": "string" }, "action": { "type": "string" }, "description": { "type": "string" } }, "required": [ "resource", "action" ] }, "AccessUpdatePermissionBodyV1": { "type": "object", "properties": { "resource": { "type": "string" }, "action": { "type": "string" }, "description": { "type": "string" } } }, "AccessPermissionChecksBodyV1": { "type": "object", "properties": { "user_id": { "type": "string", "format": "uuid" }, "permissions": { "type": "array", "items": { "type": "object", "properties": { "resource": { "type": "string" }, "action": { "type": "string" } }, "required": [ "resource", "action" ] }, "minItems": 1 } }, "required": [ "permissions" ] }, "AccessCreateTeamBodyV1": { "type": "object", "properties": { "team_name": { "type": "string" }, "manager_id": { "type": "string", "format": "uuid" }, "members": { "type": "array", "items": { "type": "object", "additionalProperties": {} } } }, "required": [ "team_name" ] }, "AccessUpdateTeamBodyV1": { "type": "object", "properties": { "team_name": { "type": "string" }, "manager_id": { "type": "string", "format": "uuid" }, "members": { "type": "array", "items": { "type": "object", "additionalProperties": {} } } } }, "AccessAddTeamMemberBodyV1": { "type": "object", "properties": { "user_id": { "type": "string", "format": "uuid" } }, "required": [ "user_id" ] }, "AccessBulkAddTeamMembersBodyV1": { "type": "object", "properties": { "user_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1 } }, "required": [ "user_ids" ] }, "AccessAssignTeamManagerBodyV1": { "type": "object", "properties": { "manager_id": { "type": "string", "format": "uuid" } }, "required": [ "manager_id" ] }, "AccessTeamPermissionGrantBodyV1": { "type": "object", "properties": { "resource": { "type": "string", "description": "Controller also accepts permission alias field." }, "permission": { "type": "string" }, "action": { "type": "string" }, "expires_at": { "type": "string" } } }, "AccessTeamHierarchyBodyV1": { "type": "object", "properties": { "parent_team_id": { "type": [ "string", "null" ], "format": "uuid" } } }, "AccessTeamBulkUpdateBodyV1": { "type": "object", "properties": { "team_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1 }, "updates": { "type": "object", "additionalProperties": {} } }, "required": [ "team_ids", "updates" ] }, "AccessTeamBulkDeleteBodyV1": { "type": "object", "properties": { "team_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1 } }, "required": [ "team_ids" ] }, "AccessApiErrorV1": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] }, "AccessApiSuccessV1": { "type": "object", "properties": { "data": { "anyOf": [ { "type": "object", "additionalProperties": {} }, { "type": "array", "items": { "type": "object", "additionalProperties": {} } } ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "AccessApiPaginatedV1": { "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object", "additionalProperties": {} } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" }, "hasNext": { "type": "boolean" }, "hasPrev": { "type": "boolean" } }, "required": [ "page", "limit", "total", "totalPages", "hasNext", "hasPrev" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data", "pagination" ] }, "AssetListQuery": { "type": "object", "properties": { "page": { "type": "string", "description": "Page number as a query string. Defaults to 1 after validation." }, "limit": { "type": "string", "description": "Page size as a query string. Must parse to 1 through 100; defaults to 25." }, "sort": { "type": "string", "description": "Sort column. Defaults to created_at." }, "order": { "type": "string", "enum": [ "asc", "desc" ], "description": "Sort direction. Defaults to desc." }, "search": { "type": "string", "description": "General search filter accepted by the shared filter schema. The current AssetService.list implementation does not apply this filter." }, "created_from": { "type": "string", "format": "date-time", "description": "Accepted by validation, but not currently applied by AssetService.list." }, "created_to": { "type": "string", "format": "date-time", "description": "Accepted by validation, but not currently applied by AssetService.list." }, "updated_from": { "type": "string", "format": "date-time", "description": "Accepted by validation, but not currently applied by AssetService.list." }, "updated_to": { "type": "string", "format": "date-time", "description": "Accepted by validation, but not currently applied by AssetService.list." }, "is_active": { "type": "string", "enum": [ "true", "false" ], "description": "Accepted by validation, but not currently applied by AssetService.list." }, "asset_tag": { "type": "string", "description": "Partial asset tag match using ILIKE." }, "name": { "type": "string", "description": "Partial asset name match using ILIKE." }, "client_id": { "type": "string", "format": "uuid", "description": "Client UUID from clients.client_id." }, "location_id": { "type": "string", "format": "uuid", "description": "Client location UUID from client_locations.location_id." }, "asset_type": { "type": "string", "enum": [ "workstation", "network_device", "server", "mobile_device", "printer", "unknown" ], "description": "Asset type stored in assets.asset_type." }, "status": { "type": "string", "description": "Exact asset status match." }, "location": { "type": "string", "description": "Partial location match using ILIKE." }, "client_name": { "type": "string", "description": "Partial client name match; joins clients on client_id and tenant." }, "has_warranty": { "type": "string", "enum": [ "true", "false" ], "description": "true requires warranty_end_date to be non-null; false requires it to be null." }, "warranty_expired": { "type": "string", "enum": [ "true", "false" ], "description": "true filters warranty_end_date before now; false filters future warranty dates or no warranty." }, "maintenance_due": { "type": "string", "enum": [ "true", "false" ], "description": "Accepted by validation, but not currently applied by AssetService.list." }, "purchase_date_from": { "type": "string", "format": "date-time", "description": "Filter purchase_date greater than or equal to this timestamp." }, "purchase_date_to": { "type": "string", "format": "date-time", "description": "Filter purchase_date less than or equal to this timestamp." }, "warranty_end_from": { "type": "string", "format": "date-time", "description": "Filter warranty_end_date greater than or equal to this timestamp." }, "warranty_end_to": { "type": "string", "format": "date-time", "description": "Filter warranty_end_date less than or equal to this timestamp." } } }, "AssetExportQuery": { "type": "object", "properties": { "format": { "type": "string", "enum": [ "csv", "json" ], "description": "Export format. csv (default) returns text/csv as an attachment; json returns the success envelope with the rows in data." }, "asset_types": { "type": "array", "items": { "type": "string", "enum": [ "workstation", "network_device", "server", "mobile_device", "printer", "unknown" ] }, "description": "Filter to these asset types. Repeat the param or pass a comma-separated list." }, "statuses": { "type": "array", "items": { "type": "string" }, "description": "Filter to these asset statuses. Repeat the param or pass a comma-separated list." }, "client_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Filter to these client UUIDs. Repeat the param or pass a comma-separated list." }, "fields": { "type": "array", "items": { "type": "string" }, "description": "Restrict exported columns to this set. Repeat the param or pass a comma-separated list." } } }, "AssetIdParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." } }, "required": [ "id" ] }, "AssetDocumentAssociationParams": { "type": "object", "properties": { "associationId": { "type": "string", "format": "uuid", "description": "Document association UUID from document_associations.association_id." } }, "required": [ "associationId" ] }, "AssetDocumentAssociationRequest": { "type": "object", "properties": { "document_id": { "type": "string", "format": "uuid", "description": "Document UUID from documents.document_id to associate with the asset." }, "notes": { "type": "string", "description": "Optional notes stored on document_associations.notes." } }, "required": [ "document_id" ] }, "AssetExtensionData": { "type": "object", "additionalProperties": {}, "description": "Asset-type-specific extension data written to the corresponding extension table for workstation, network device, server, mobile device, or printer assets." }, "AssetCreateRequest": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid", "description": "Client UUID from clients.client_id. Required." }, "asset_type": { "type": "string", "enum": [ "workstation", "network_device", "server", "mobile_device", "printer", "unknown" ], "description": "Asset type. Determines the optional extension data table." }, "asset_tag": { "type": "string", "minLength": 1, "maxLength": 255, "description": "Required tenant-specific asset tag." }, "name": { "type": "string", "minLength": 1, "maxLength": 255, "description": "Required asset name." }, "status": { "type": "string", "minLength": 1, "description": "Required asset status." }, "location_id": { "type": [ "string", "null" ], "format": "uuid", "description": "Optional client_locations.location_id link. Validated to belong to client_id." }, "location": { "type": "string", "description": "Optional free-text asset location." }, "serial_number": { "type": "string", "description": "Optional serial number." }, "purchase_date": { "type": "string", "format": "date-time", "description": "Optional purchase date/time." }, "warranty_end_date": { "type": "string", "format": "date-time", "description": "Optional warranty end date/time." }, "extension_data": { "$ref": "#/components/schemas/AssetExtensionData" } }, "required": [ "client_id", "asset_type", "asset_tag", "name", "status" ] }, "AssetUpdateData": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid", "description": "Client UUID to assign to the asset." }, "asset_type": { "type": "string", "enum": [ "workstation", "network_device", "server", "mobile_device", "printer", "unknown" ], "description": "Asset type to store in assets.asset_type." }, "asset_tag": { "type": "string", "minLength": 1, "maxLength": 255, "description": "Tenant-specific asset tag." }, "name": { "type": "string", "minLength": 1, "maxLength": 255, "description": "Asset name." }, "status": { "type": "string", "minLength": 1, "description": "Asset status." }, "location_id": { "type": [ "string", "null" ], "format": "uuid", "description": "Client_locations.location_id link. Pass null to clear." }, "location": { "type": "string", "description": "Free-text asset location." }, "serial_number": { "type": "string", "description": "Serial number." }, "purchase_date": { "type": "string", "format": "date-time", "description": "Purchase date/time." }, "warranty_end_date": { "type": "string", "format": "date-time", "description": "Warranty end date/time." } } }, "AssetBulkUpdateRequest": { "type": "object", "properties": { "assets": { "type": "array", "items": { "type": "object", "properties": { "asset_id": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "data": { "allOf": [ { "$ref": "#/components/schemas/AssetUpdateData" }, { "description": "Partial update data validated with updateAssetSchema." } ] } }, "required": [ "asset_id", "data" ] }, "minItems": 1, "maxItems": 50, "description": "Assets to update. Limited to 1 through 50 entries by validation." } }, "required": [ "assets" ] }, "AssetBulkStatusRequest": { "type": "object", "properties": { "asset_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1, "maxItems": 50, "description": "Asset UUIDs from assets.asset_id. Limited to 1 through 50 entries by validation." }, "status": { "type": "string", "minLength": 1, "description": "New status assigned to every asset in asset_ids." } }, "required": [ "asset_ids", "status" ] }, "HateoasLink": { "type": "object", "properties": { "href": { "type": "string", "description": "Target URL for the related operation." }, "method": { "type": "string", "description": "HTTP method for the link when supplied." } }, "required": [ "href" ] }, "AssetLinks": { "type": "object", "properties": { "self": { "$ref": "#/components/schemas/HateoasLink" }, "edit": { "$ref": "#/components/schemas/HateoasLink" }, "delete": { "$ref": "#/components/schemas/HateoasLink" }, "list": { "$ref": "#/components/schemas/HateoasLink" }, "documents": { "$ref": "#/components/schemas/HateoasLink" }, "maintenance": { "$ref": "#/components/schemas/HateoasLink" }, "history": { "$ref": "#/components/schemas/HateoasLink" } }, "description": "HATEOAS links generated from the asset_id." }, "AssetDocumentAssociationRow": { "type": "object", "properties": { "association_id": { "type": "string", "format": "uuid", "description": "Primary key from document_associations.association_id." }, "entity_type": { "type": "string", "enum": [ "asset" ], "description": "Entity type stored on document_associations; always asset for these routes." }, "entity_id": { "type": "string", "format": "uuid", "description": "Asset UUID from document_associations.entity_id." }, "document_id": { "type": "string", "format": "uuid", "description": "Document UUID from document_associations.document_id." }, "notes": { "type": [ "string", "null" ], "description": "Optional association notes." }, "tenant": { "type": "string", "format": "uuid", "description": "Tenant UUID from document_associations.tenant." }, "created_at": { "type": "string", "format": "date-time", "description": "Association creation timestamp." }, "original_filename": { "type": "string", "description": "Original filename from the joined documents table, present on list responses." }, "file_size": { "type": "number", "description": "File size in bytes from the joined documents table, present on list responses." }, "mime_type": { "type": "string", "description": "MIME type from the joined documents table, present on list responses." }, "uploaded_at": { "type": "string", "format": "date-time", "description": "Document upload timestamp from the joined documents table, present on list responses." } }, "required": [ "association_id", "entity_type", "entity_id", "document_id", "tenant" ] }, "AssetDocumentListPayload": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/AssetDocumentAssociationRow" }, "description": "Document association rows for the asset. Empty when no associations exist or the asset is not found." }, "_links": { "type": "object", "properties": { "self": { "$ref": "#/components/schemas/HateoasLink" }, "create": { "$ref": "#/components/schemas/HateoasLink" }, "parent": { "$ref": "#/components/schemas/HateoasLink" } }, "description": "Collection links. The controller currently points these at /api/v2/assets paths." } }, "required": [ "data", "_links" ] }, "AssetDocumentListResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "$ref": "#/components/schemas/AssetDocumentListPayload" }, "meta": { "type": "object", "properties": { "timestamp": { "type": "string", "format": "date-time" }, "version": { "type": "string" } }, "required": [ "timestamp", "version" ] } }, "required": [ "success", "data", "meta" ] }, "AssetDocumentAssociationPayload": { "type": "object", "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/AssetDocumentAssociationRow" }, { "description": "Inserted document association row returned from document_associations.returning(*)." } ] } }, "required": [ "data" ] }, "AssetDocumentAssociationResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "$ref": "#/components/schemas/AssetDocumentAssociationPayload" }, "meta": { "type": "object", "properties": { "timestamp": { "type": "string", "format": "date-time" }, "version": { "type": "string" } }, "required": [ "timestamp", "version" ] } }, "required": [ "success", "data", "meta" ] }, "AssetResource": { "type": "object", "properties": { "asset_id": { "type": "string", "format": "uuid", "description": "Primary key from assets.asset_id." }, "client_id": { "type": "string", "format": "uuid", "description": "Client UUID from assets.client_id." }, "asset_type": { "type": "string", "enum": [ "workstation", "network_device", "server", "mobile_device", "printer", "unknown" ], "description": "Asset type stored in assets.asset_type." }, "asset_tag": { "type": "string", "description": "Tenant-specific asset tag." }, "name": { "type": "string", "description": "Asset display name." }, "status": { "type": "string", "description": "Asset status." }, "location_id": { "type": [ "string", "null" ], "format": "uuid", "description": "Client_locations.location_id link, when set." }, "location": { "type": [ "string", "null" ], "description": "Asset location, when recorded." }, "serial_number": { "type": [ "string", "null" ], "description": "Asset serial number, when recorded." }, "purchase_date": { "type": [ "string", "null" ], "description": "Asset purchase date from assets.purchase_date." }, "warranty_end_date": { "type": [ "string", "null" ], "description": "Warranty end date from assets.warranty_end_date." }, "created_at": { "type": "string", "format": "date-time", "description": "Asset creation timestamp." }, "updated_at": { "type": "string", "format": "date-time", "description": "Asset last update timestamp." }, "tenant": { "type": "string", "format": "uuid", "description": "Tenant UUID from assets.tenant; filtered to the authenticated request context." }, "client_name": { "type": "string", "description": "Client name selected from the joined clients table." }, "warranty_status": { "type": "string", "enum": [ "no_warranty", "expired", "expiring_soon", "active" ], "description": "Computed from warranty_end_date by SQL CASE expression." }, "maintenance_status": { "type": "string", "description": "Optional computed maintenance status when present in service results." }, "extension_data": { "allOf": [ { "$ref": "#/components/schemas/AssetExtensionData" }, { "type": [ "object", "null" ], "description": "Asset-type-specific extension data returned by getWithDetails after create." } ] }, "relationships": { "type": "array", "items": { "type": "object", "additionalProperties": {} }, "description": "Related asset rows included by getWithDetails after create." }, "documents": { "type": "array", "items": { "type": "object", "additionalProperties": {} }, "description": "Associated document rows included by getWithDetails after create." }, "maintenance_schedules": { "type": "array", "items": { "type": "object", "additionalProperties": {} }, "description": "Maintenance schedule rows included by getWithDetails after create." }, "_links": { "$ref": "#/components/schemas/AssetLinks" } }, "required": [ "asset_id", "client_id", "asset_type", "asset_tag", "name", "status", "created_at", "updated_at", "tenant" ] }, "AssetListPagination": { "type": "object", "properties": { "page": { "type": "integer", "description": "Current page number." }, "limit": { "type": "integer", "description": "Page size." }, "total": { "type": "integer", "description": "Total matching asset count." }, "totalPages": { "type": "integer", "description": "Total number of pages calculated from total and limit." } }, "required": [ "page", "limit", "total", "totalPages" ] }, "AssetCollectionLinks": { "type": "object", "properties": { "self": { "$ref": "#/components/schemas/HateoasLink" }, "create": { "$ref": "#/components/schemas/HateoasLink" }, "search": { "$ref": "#/components/schemas/HateoasLink" }, "export": { "$ref": "#/components/schemas/HateoasLink" }, "stats": { "$ref": "#/components/schemas/HateoasLink" } }, "description": "Collection links returned by ApiAssetController.list. These currently point at /api/v2/assets paths." }, "ApiResponseMeta": { "type": "object", "properties": { "timestamp": { "type": "string", "format": "date-time", "description": "Response timestamp generated by createApiResponse/createErrorResponse." }, "version": { "type": "string", "description": "API response version string." } }, "required": [ "timestamp", "version" ] }, "AssetListPayload": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/AssetResource" }, "description": "Asset records for the requested page." }, "pagination": { "$ref": "#/components/schemas/AssetListPagination" }, "_links": { "$ref": "#/components/schemas/AssetCollectionLinks" } }, "required": [ "data", "pagination", "_links" ] }, "AssetListResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Indicates the controller returned a successful response envelope." }, "data": { "allOf": [ { "$ref": "#/components/schemas/AssetListPayload" }, { "description": "Nested payload passed to createApiResponse by ApiAssetController.list." } ] }, "meta": { "$ref": "#/components/schemas/ApiResponseMeta" } }, "required": [ "success", "data", "meta" ] }, "AssetResourcePayload": { "type": "object", "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/AssetResource" }, { "description": "Asset record returned by the service." } ] } }, "required": [ "data" ] }, "AssetResourceResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "allOf": [ { "$ref": "#/components/schemas/AssetResourcePayload" }, { "description": "Nested payload passed to createApiResponse by ApiAssetController.create." } ] }, "meta": { "$ref": "#/components/schemas/ApiResponseMeta" } }, "required": [ "success", "data", "meta" ] }, "AssetBulkUpdatePayload": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/AssetResource" }, "description": "Updated asset rows returned from AssetService.update." }, "message": { "type": "string", "description": "Human-readable count of updated assets." } }, "required": [ "data", "message" ] }, "AssetBulkUpdateResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "$ref": "#/components/schemas/AssetBulkUpdatePayload" }, "meta": { "$ref": "#/components/schemas/ApiResponseMeta" } }, "required": [ "success", "data", "meta" ] }, "AssetExportJsonPayload": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/AssetResource" }, "description": "All asset rows returned by AssetService.list with default list options. Export filters are currently not applied." } }, "required": [ "data" ] }, "AssetExportJsonResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "$ref": "#/components/schemas/AssetExportJsonPayload" }, "meta": { "$ref": "#/components/schemas/ApiResponseMeta" } }, "required": [ "success", "data", "meta" ] }, "AssetApiErrorEnvelope": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ false ], "description": "Indicates the API response is an error envelope." }, "error": { "type": "object", "properties": { "message": { "type": "string", "description": "Human-readable error message." }, "code": { "type": "string", "description": "Machine-readable error code such as VALIDATION_ERROR or INTERNAL_ERROR." }, "details": { "description": "Optional structured details, including Zod validation errors." } }, "required": [ "message", "code" ] }, "meta": { "$ref": "#/components/schemas/ApiResponseMeta" } }, "required": [ "success", "error" ] }, "AssetMiddlewareUnauthorizedResponse": { "type": "object", "properties": { "error": { "type": "string", "description": "Middleware-level error, usually Unauthorized: API key missing." } }, "required": [ "error" ] }, "AssetMaintenanceScheduleParams": { "type": "object", "properties": { "scheduleId": { "type": "string", "format": "uuid", "description": "Maintenance schedule UUID from asset_maintenance_schedules.schedule_id." } }, "required": [ "scheduleId" ] }, "AssetRelationshipParams": { "type": "object", "properties": { "relationshipId": { "type": "string", "format": "uuid", "description": "Asset relationship UUID from asset_relationships.relationship_id." } }, "required": [ "relationshipId" ] }, "AssetMaintenanceScheduleUpdateRequest": { "type": "object", "properties": { "schedule_type": { "type": "string", "enum": [ "preventive", "inspection", "calibration", "replacement" ], "description": "Type of maintenance schedule." }, "frequency": { "type": "string", "enum": [ "daily", "weekly", "monthly", "quarterly", "yearly", "custom" ], "description": "Recurrence frequency. Changing this triggers next_maintenance recalculation." }, "frequency_interval": { "type": "number", "minimum": 1, "description": "Frequency multiplier used when calculating the next maintenance date." }, "start_date": { "type": "string", "format": "date-time", "description": "Schedule start date/time. Changing this triggers next_maintenance recalculation." }, "end_date": { "type": "string", "format": "date-time", "description": "Optional schedule end date/time." }, "notes": { "type": "string", "description": "Free-text schedule notes." }, "assigned_to": { "type": "string", "format": "uuid", "description": "Assigned user UUID from users.user_id." }, "is_active": { "type": "boolean", "description": "Whether the schedule is active." }, "schedule_config": { "type": "object", "additionalProperties": {}, "description": "Custom scheduling configuration." } } }, "AssetMaintenanceScheduleResource": { "type": "object", "properties": { "schedule_id": { "type": "string", "format": "uuid", "description": "Primary key from asset_maintenance_schedules.schedule_id." }, "asset_id": { "type": "string", "format": "uuid", "description": "Asset UUID from asset_maintenance_schedules.asset_id." }, "schedule_type": { "type": "string", "description": "Maintenance schedule type as used by ApiAssetController." }, "frequency": { "type": "string", "description": "Recurrence frequency." }, "frequency_interval": { "type": [ "number", "null" ], "description": "Frequency multiplier." }, "start_date": { "type": [ "string", "null" ], "description": "Schedule start date/time." }, "end_date": { "type": [ "string", "null" ], "description": "Schedule end date/time." }, "last_maintenance": { "type": [ "string", "null" ], "description": "Last recorded maintenance date/time." }, "next_maintenance": { "type": [ "string", "null" ], "description": "Next calculated maintenance date/time." }, "notes": { "type": [ "string", "null" ], "description": "Free-text schedule notes." }, "assigned_to": { "type": [ "string", "null" ], "format": "uuid", "description": "Assigned user UUID." }, "is_active": { "type": "boolean", "description": "Whether the schedule is active." }, "schedule_config": { "type": [ "object", "null" ], "additionalProperties": {}, "description": "Custom scheduling configuration." }, "created_at": { "type": "string", "format": "date-time", "description": "Creation timestamp." }, "updated_at": { "type": "string", "format": "date-time", "description": "Last update timestamp." }, "tenant": { "type": "string", "format": "uuid", "description": "Tenant UUID scoped from the request context." } }, "required": [ "schedule_id", "asset_id", "tenant" ] }, "AssetMaintenanceSchedulePayload": { "type": "object", "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/AssetMaintenanceScheduleResource" }, { "description": "Updated schedule row. Undefined when the schedule ID does not exist or belongs to another tenant." } ] } } }, "AssetMaintenanceScheduleResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "$ref": "#/components/schemas/AssetMaintenanceSchedulePayload" }, "meta": { "$ref": "#/components/schemas/ApiResponseMeta" } }, "required": [ "success", "data", "meta" ] }, "AssetMaintenanceScheduleCreateRequest": { "type": "object", "properties": { "schedule_type": { "type": "string", "enum": [ "preventive", "inspection", "calibration", "replacement" ], "description": "Required type of maintenance schedule." }, "frequency": { "type": "string", "enum": [ "daily", "weekly", "monthly", "quarterly", "yearly", "custom" ], "description": "Required recurrence frequency used to calculate next_maintenance." }, "frequency_interval": { "type": "number", "minimum": 1, "description": "Frequency multiplier. Defaults to service logic when absent." }, "start_date": { "type": "string", "format": "date-time", "description": "Schedule start date/time. The current schema makes this optional; the service falls back to now when absent." }, "end_date": { "type": "string", "format": "date-time", "description": "Optional schedule end date/time." }, "notes": { "type": "string", "description": "Free-text schedule notes." }, "assigned_to": { "type": "string", "format": "uuid", "description": "Assigned user UUID from users.user_id." }, "is_active": { "type": "boolean", "description": "Whether the schedule is active. Defaults to true in validation." }, "schedule_config": { "type": "object", "additionalProperties": {}, "description": "Custom scheduling configuration." } }, "required": [ "schedule_type", "frequency" ] }, "AssetMaintenanceScheduleListPayload": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/AssetMaintenanceScheduleResource" }, "description": "Maintenance schedule rows for the asset. Empty when no schedules exist or the asset is not found." }, "_links": { "type": "object", "properties": { "self": { "$ref": "#/components/schemas/HateoasLink" }, "create": { "$ref": "#/components/schemas/HateoasLink" }, "history": { "$ref": "#/components/schemas/HateoasLink" }, "parent": { "$ref": "#/components/schemas/HateoasLink" } }, "description": "Collection links. The controller currently points these at /api/v2/assets paths." } }, "required": [ "data", "_links" ] }, "AssetMaintenanceScheduleListResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "$ref": "#/components/schemas/AssetMaintenanceScheduleListPayload" }, "meta": { "$ref": "#/components/schemas/ApiResponseMeta" } }, "required": [ "success", "data", "meta" ] }, "AssetMaintenanceHistoryResource": { "type": "object", "properties": { "history_id": { "type": "string", "format": "uuid", "description": "Primary key from asset_maintenance_history.history_id." }, "asset_id": { "type": "string", "format": "uuid", "description": "Asset UUID from asset_maintenance_history.asset_id." }, "schedule_id": { "type": [ "string", "null" ], "format": "uuid", "description": "Optional related maintenance schedule UUID." }, "maintenance_type": { "anyOf": [ { "type": "string", "enum": [ "preventive", "corrective", "inspection", "calibration", "replacement" ] }, { "type": "string" } ], "description": "Maintenance type recorded for the history row." }, "performed_by": { "type": "string", "format": "uuid", "description": "User UUID supplied in the maintenance record." }, "performed_at": { "type": "string", "format": "date-time", "description": "Timestamp when maintenance was performed." }, "duration_hours": { "type": [ "number", "null" ], "description": "Maintenance duration in hours, when recorded." }, "cost": { "type": [ "number", "null" ], "description": "Maintenance cost, when recorded." }, "notes": { "type": [ "string", "null" ], "description": "Free-text notes." }, "parts_used": { "type": [ "array", "null" ], "items": { "type": "string" }, "description": "Parts used, when recorded." }, "maintenance_data": { "type": [ "object", "null" ], "additionalProperties": {}, "description": "Arbitrary structured maintenance data." }, "description": { "type": [ "string", "null" ], "description": "Description column when present in the maintenance history table." }, "created_at": { "type": "string", "format": "date-time", "description": "History creation timestamp." }, "tenant": { "type": "string", "format": "uuid", "description": "Tenant UUID scoped from the request context." }, "performed_by_user_name": { "type": [ "string", "null" ], "description": "Concatenated user name from the joined users table." } }, "required": [ "history_id", "asset_id", "maintenance_type", "performed_by", "performed_at", "created_at", "tenant" ] }, "AssetMaintenanceHistoryPayload": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/AssetMaintenanceHistoryResource" }, "description": "Maintenance history rows ordered by performed_at descending." }, "_links": { "type": "object", "properties": { "self": { "$ref": "#/components/schemas/HateoasLink" }, "schedules": { "$ref": "#/components/schemas/HateoasLink" }, "parent": { "$ref": "#/components/schemas/HateoasLink" } }, "description": "History links. The controller currently points these at /api/v2/assets paths." } }, "required": [ "data", "_links" ] }, "AssetMaintenanceHistoryResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "$ref": "#/components/schemas/AssetMaintenanceHistoryPayload" }, "meta": { "$ref": "#/components/schemas/ApiResponseMeta" } }, "required": [ "success", "data", "meta" ] }, "AssetRecordMaintenanceRequest": { "type": "object", "properties": { "schedule_id": { "type": "string", "format": "uuid", "description": "Optional maintenance schedule UUID. If present and found for the tenant, the schedule last_maintenance and next_maintenance are updated." }, "maintenance_type": { "type": "string", "enum": [ "preventive", "corrective", "inspection", "calibration", "replacement" ], "description": "Required type of maintenance performed." }, "performed_by": { "type": "string", "format": "uuid", "description": "Required UUID of the user who performed maintenance. The service does not validate this user before insert." }, "performed_at": { "type": "string", "format": "date-time", "description": "Required timestamp when maintenance was performed." }, "duration_hours": { "type": "number", "minimum": 0, "description": "Optional duration in hours." }, "cost": { "type": "number", "minimum": 0, "description": "Optional cost." }, "notes": { "type": "string", "description": "Optional notes." }, "parts_used": { "type": "array", "items": { "type": "string" }, "description": "Optional list of parts used." }, "maintenance_data": { "type": "object", "additionalProperties": {}, "description": "Optional structured maintenance data." } }, "required": [ "maintenance_type", "performed_by", "performed_at" ] }, "AssetRecordMaintenancePayload": { "type": "object", "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/AssetMaintenanceHistoryResource" }, { "description": "Inserted asset_maintenance_history row returned from the database." } ] } }, "required": [ "data" ] }, "AssetRecordMaintenanceResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "$ref": "#/components/schemas/AssetRecordMaintenancePayload" }, "meta": { "$ref": "#/components/schemas/ApiResponseMeta" } }, "required": [ "success", "data", "meta" ] }, "AssetRelationshipCreateRequest": { "type": "object", "properties": { "related_asset_id": { "type": "string", "format": "uuid", "description": "Related asset UUID from assets.asset_id." }, "relationship_type": { "type": "string", "minLength": 1, "description": "Free-form relationship type. Must be non-empty." } }, "required": [ "related_asset_id", "relationship_type" ] }, "AssetRelationshipRow": { "type": "object", "properties": { "relationship_id": { "type": "string", "format": "uuid", "description": "Primary key from asset_relationships.relationship_id." }, "asset_id": { "type": "string", "format": "uuid", "description": "Source asset UUID from asset_relationships.asset_id." }, "related_asset_id": { "type": "string", "format": "uuid", "description": "Related asset UUID from asset_relationships.related_asset_id." }, "relationship_type": { "type": "string", "description": "Free-form relationship type." }, "tenant": { "type": "string", "format": "uuid", "description": "Tenant UUID from asset_relationships.tenant." }, "created_at": { "type": "string", "format": "date-time", "description": "Relationship creation timestamp." }, "asset_tag": { "type": "string", "description": "Related asset tag from joined related_assets, present in list responses." }, "related_asset_name": { "type": "string", "description": "Related asset name from joined related_assets, present in list responses." }, "asset_type": { "type": "string", "enum": [ "workstation", "network_device", "server", "mobile_device", "printer", "unknown" ], "description": "Related asset type from joined related_assets, present in list responses." }, "status": { "type": "string", "description": "Related asset status from joined related_assets, present in list responses." } }, "required": [ "relationship_id", "asset_id", "related_asset_id", "relationship_type", "tenant" ] }, "AssetRelationshipListPayload": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/AssetRelationshipRow" }, "description": "Relationship rows for the asset. Empty when no relationships exist or the asset is not found." }, "_links": { "type": "object", "properties": { "self": { "$ref": "#/components/schemas/HateoasLink" }, "create": { "$ref": "#/components/schemas/HateoasLink" }, "parent": { "$ref": "#/components/schemas/HateoasLink" } }, "description": "Relationship links. The controller currently points these at /api/v2/assets paths." } }, "required": [ "data", "_links" ] }, "AssetRelationshipListResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "$ref": "#/components/schemas/AssetRelationshipListPayload" }, "meta": { "$ref": "#/components/schemas/ApiResponseMeta" } }, "required": [ "success", "data", "meta" ] }, "AssetRelationshipPayload": { "type": "object", "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/AssetRelationshipRow" }, { "description": "Inserted asset_relationships row returned from the database." } ] } }, "required": [ "data" ] }, "AssetRelationshipResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "$ref": "#/components/schemas/AssetRelationshipPayload" }, "meta": { "$ref": "#/components/schemas/ApiResponseMeta" } }, "required": [ "success", "data", "meta" ] }, "AssetSearchQuery": { "type": "object", "properties": { "query": { "type": "string", "minLength": 1, "description": "Required search term used in ILIKE clauses." }, "fields": { "type": "array", "items": { "type": "string", "enum": [ "asset_tag", "name", "serial_number", "location", "client_name" ] }, "description": "Search fields. If omitted, the service searches asset_tag, name, serial_number, and location." }, "asset_types": { "type": "array", "items": { "type": "string", "enum": [ "workstation", "network_device", "server", "mobile_device", "printer", "unknown" ] }, "description": "Optional asset type filters." }, "statuses": { "type": "array", "items": { "type": "string" }, "description": "Optional status filters." }, "client_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Optional client UUID filters." }, "include_extension_data": { "type": "string", "enum": [ "true", "false" ], "description": "When true, the service fetches asset-type-specific extension data for every result." }, "limit": { "type": "string", "description": "Maximum result count as a query string. Must parse to 1 through 100; defaults to 25." } }, "required": [ "query" ] }, "AssetSearchPayload": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/AssetResource" }, "description": "Matching assets with HATEOAS links." }, "_links": { "type": "object", "properties": { "self": { "$ref": "#/components/schemas/HateoasLink" } }, "description": "Search collection link. The controller currently points this at /api/v2/assets/search." } }, "required": [ "data", "_links" ] }, "AssetSearchResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "$ref": "#/components/schemas/AssetSearchPayload" }, "meta": { "$ref": "#/components/schemas/ApiResponseMeta" } }, "required": [ "success", "data", "meta" ] }, "AssetStatsPayload": { "type": "object", "properties": { "data": { "type": "object", "properties": { "total_assets": { "type": "integer", "description": "Total asset count for the tenant." }, "assets_added_this_month": { "type": "integer", "description": "Count of assets created since the start of the current month." }, "average_asset_age_days": { "type": [ "integer", "null" ], "description": "Rounded average age in days from purchase_date, or null when unavailable." }, "total_asset_value": { "type": "number", "description": "Sum of purchase_price values, treating null as zero." }, "assets_by_type": { "type": "object", "additionalProperties": { "type": "integer" }, "description": "Counts grouped by assets.asset_type." }, "assets_by_status": { "type": "object", "additionalProperties": { "type": "integer" }, "description": "Counts grouped by assets.status." }, "assets_by_client": { "type": "object", "additionalProperties": { "type": "integer" }, "description": "Top client counts grouped by clients.client_name. The service limits this grouping to 10 clients." }, "warranty_expiring_soon": { "type": "integer", "description": "Count of warranties ending within 30 days." }, "warranty_expired": { "type": "integer", "description": "Count of warranties already expired." }, "maintenance_due": { "type": "integer", "description": "Count of active maintenance schedules with next_maintenance due now or earlier." }, "maintenance_overdue": { "type": "integer", "description": "Count of active maintenance schedules overdue by more than seven days." } }, "required": [ "total_assets", "assets_added_this_month", "average_asset_age_days", "total_asset_value", "assets_by_type", "assets_by_status", "assets_by_client", "warranty_expiring_soon", "warranty_expired", "maintenance_due", "maintenance_overdue" ] }, "_links": { "type": "object", "properties": { "self": { "$ref": "#/components/schemas/HateoasLink" }, "assets": { "$ref": "#/components/schemas/HateoasLink" } }, "description": "Stats links. The controller currently points these at /api/v2/assets paths." } }, "required": [ "data", "_links" ] }, "AssetStatsResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "$ref": "#/components/schemas/AssetStatsPayload" }, "meta": { "$ref": "#/components/schemas/ApiResponseMeta" } }, "required": [ "success", "data", "meta" ] }, "AssetTicketRow": { "type": "object", "properties": { "ticket_id": { "type": "string", "format": "uuid", "description": "Ticket UUID from tickets.ticket_id." }, "ticket_number": { "type": "string", "description": "Human-readable ticket number from tickets.ticket_number." }, "title": { "type": [ "string", "null" ], "description": "Ticket title." }, "status_id": { "type": [ "string", "null" ], "format": "uuid", "description": "Status UUID from tickets.status_id." }, "status_name": { "type": [ "string", "null" ], "description": "Status name from the joined statuses table." }, "is_closed": { "type": [ "boolean", "null" ], "description": "Whether the ticket is closed." }, "priority_id": { "type": [ "string", "null" ], "format": "uuid", "description": "Priority UUID from tickets.priority_id." }, "assigned_to": { "type": [ "string", "null" ], "format": "uuid", "description": "Assigned user UUID from tickets.assigned_to." }, "client_id": { "type": [ "string", "null" ], "format": "uuid", "description": "Client UUID from tickets.client_id." }, "board_id": { "type": [ "string", "null" ], "format": "uuid", "description": "Board UUID from tickets.board_id." }, "entered_at": { "type": [ "string", "null" ], "description": "Ticket creation timestamp." }, "updated_at": { "type": [ "string", "null" ], "description": "Ticket last update timestamp." }, "relationship_type": { "type": "string", "description": "Association type from asset_associations.relationship_type (e.g. affected, related)." }, "association_notes": { "type": [ "string", "null" ], "description": "Optional notes from asset_associations.notes." }, "linked_at": { "type": [ "string", "null" ], "description": "When the asset was linked to the ticket, from asset_associations.created_at." } }, "required": [ "ticket_id", "ticket_number", "relationship_type" ] }, "AssetTicketListResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "type": "array", "items": { "$ref": "#/components/schemas/AssetTicketRow" }, "description": "Tickets linked to the asset via asset_associations, ordered by ticket updated_at descending. Empty when no associations exist or the asset is not found." }, "meta": { "$ref": "#/components/schemas/ApiResponseMeta" } }, "required": [ "success", "data", "meta" ] }, "TicketAssetRow": { "allOf": [ { "$ref": "#/components/schemas/AssetResource" }, { "type": "object", "properties": { "relationship_type": { "type": "string", "description": "Association type from asset_associations.relationship_type (e.g. affected, related)." }, "association_notes": { "type": [ "string", "null" ], "description": "Optional notes from asset_associations.notes." }, "linked_at": { "type": [ "string", "null" ], "description": "When the asset was linked to the ticket, from asset_associations.created_at." } }, "required": [ "relationship_type" ] } ] }, "TicketAssetListResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/TicketAssetRow" }, "description": "Assets linked to the ticket via asset_associations, ordered by link time descending. Empty when no associations exist or the ticket is not found." }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "AssetAssociationRow": { "type": "object", "properties": { "tenant": { "type": "string", "format": "uuid", "description": "Tenant UUID from asset_associations.tenant." }, "asset_id": { "type": "string", "format": "uuid", "description": "Asset UUID from asset_associations.asset_id." }, "entity_id": { "type": "string", "format": "uuid", "description": "Linked entity UUID — the ticket_id, from asset_associations.entity_id." }, "entity_type": { "type": "string", "enum": [ "ticket" ], "description": "Always 'ticket' for these endpoints." }, "relationship_type": { "type": "string", "description": "Association type from asset_associations.relationship_type (defaults to affected)." }, "notes": { "type": [ "string", "null" ], "description": "Optional notes from asset_associations.notes." }, "created_by": { "type": "string", "format": "uuid", "description": "Creator user UUID from asset_associations.created_by." }, "created_at": { "type": "string", "description": "Link creation timestamp from asset_associations.created_at." } }, "required": [ "tenant", "asset_id", "entity_id", "entity_type", "relationship_type", "created_by", "created_at" ] }, "AssetTicketLinkRequest": { "type": "object", "properties": { "ticket_id": { "type": "string", "format": "uuid", "description": "Ticket UUID to link to the asset." }, "relationship_type": { "type": "string", "minLength": 1, "description": "Optional association type; defaults to affected." }, "notes": { "type": "string", "description": "Optional notes stored on asset_associations.notes." } }, "required": [ "ticket_id" ] }, "TicketAssetLinkRequest": { "type": "object", "properties": { "asset_id": { "type": "string", "format": "uuid", "description": "Asset UUID to link to the ticket." }, "relationship_type": { "type": "string", "minLength": 1, "description": "Optional association type; defaults to affected." }, "notes": { "type": "string", "description": "Optional notes stored on asset_associations.notes." } }, "required": [ "asset_id" ] }, "AssetTicketLinkResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "allOf": [ { "$ref": "#/components/schemas/AssetAssociationRow" }, { "description": "The inserted asset_associations row." } ] }, "meta": { "$ref": "#/components/schemas/ApiResponseMeta" } }, "required": [ "success", "data", "meta" ] }, "TicketAssetLinkResponse": { "type": "object", "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/AssetAssociationRow" }, { "description": "The inserted asset_associations row." } ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "AssetTicketLinkParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "ticketId": { "type": "string", "format": "uuid", "description": "Ticket UUID from tickets.ticket_id." } }, "required": [ "id", "ticketId" ] }, "TicketAssetLinkParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Ticket UUID from tickets.ticket_id." }, "assetId": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." } }, "required": [ "id", "assetId" ] }, "AssetNotesResponse": { "type": "object", "properties": { "document": { "type": [ "object", "null" ], "additionalProperties": {} }, "blockData": { "description": "Parsed BlockNote JSON, or null when no notes exist." }, "lastUpdated": { "type": [ "string", "null" ] } } }, "AssetNotesUpdateBody": { "type": "object", "properties": { "blockData": { "description": "BlockNote JSON block data to store as the asset notes." } } }, "AssetRmmResponse": { "type": "object", "properties": { "data": { "type": [ "object", "null" ], "additionalProperties": {}, "description": "Cached RMM device data, or null (always null on Community Edition)." } }, "required": [ "data" ] }, "AssetRmmActionResponse": { "type": "object", "properties": { "success": { "type": "boolean" }, "message": { "type": "string" }, "jobId": { "type": "string", "description": "Provider job id when the action was queued (EE)." } }, "required": [ "success", "message" ] }, "AssetRemoteControlResponse": { "type": [ "object", "null" ], "properties": { "url": { "type": "string" }, "type": { "type": "string" } }, "description": "Remote-control session URL, or null when unavailable (always null on Community Edition)." }, "AssetRmmScriptBody": { "type": "object", "properties": { "scriptId": { "type": "string", "description": "Provider script identifier to run on the device." } }, "required": [ "scriptId" ] }, "AssetRemoteControlQuery": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "splashtop", "teamviewer", "vnc", "rdp", "shell" ], "description": "Remote-control protocol (default splashtop)." } } }, "AssetSoftwareQuery": { "type": "object", "properties": { "page": { "type": "integer", "minimum": 1 }, "limit": { "type": "integer", "minimum": 1, "maximum": 200 }, "category": { "type": "string" }, "software_type": { "type": "string" }, "search": { "type": "string" }, "include_uninstalled": { "type": "string", "enum": [ "true", "false" ] } } }, "AssetSoftwareResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object", "additionalProperties": {} } }, "summary": { "type": "object", "additionalProperties": {} }, "pagination": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "AssetSummaryResponse": { "type": "object", "properties": { "health_status": { "type": "string", "enum": [ "healthy", "warning", "critical", "unknown" ] }, "health_reason": { "type": "string" }, "security_status": { "type": "string", "enum": [ "secure", "at_risk", "critical" ] }, "security_issues": { "type": "array", "items": { "type": "string" } }, "warranty_status": { "type": "string", "enum": [ "no_warranty", "expired", "expiring_soon", "active" ] }, "warranty_days_remaining": { "type": [ "number", "null" ] }, "open_tickets_count": { "type": "number" } } }, "AutomationExecutionParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Automation execution UUID from automation_executions.execution_id." } }, "required": [ "id" ] }, "AutomationRuleParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Automation rule UUID from automation_rules.rule_id." } }, "required": [ "id" ] }, "AutomationTemplateParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Automation template UUID from automation_templates.template_id." } }, "required": [ "id" ] }, "AutomationExecutionsListQuery": { "type": "object", "properties": { "page": { "type": "string", "description": "Page number as a query string. Defaults to 1." }, "limit": { "type": "string", "description": "Page size as a query string. Must parse to 1 through 100; defaults to 25." }, "sort": { "type": "string", "description": "Accepted by shared list query validation; the service currently orders executions by started_at desc." }, "order": { "type": "string", "enum": [ "asc", "desc" ], "description": "Accepted by shared list query validation." }, "search": { "type": "string", "description": "Accepted by shared filter validation." }, "created_from": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "created_to": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "updated_from": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "updated_to": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "is_active": { "type": "string", "enum": [ "true", "false" ], "description": "Accepted by shared filter validation and transformed to boolean." }, "automation_rule_id": { "type": "string", "format": "uuid", "description": "Filter executions by parent automation rule UUID." }, "status": { "type": "string", "enum": [ "pending", "running", "completed", "failed", "cancelled", "timeout", "skipped" ], "description": "Filter by execution status." }, "started_from": { "type": "string", "format": "date-time", "description": "Filter executions started at or after this timestamp." }, "started_to": { "type": "string", "format": "date-time", "description": "Filter executions started at or before this timestamp." }, "duration_min_ms": { "type": "number", "minimum": 0, "description": "Minimum execution duration in milliseconds." }, "duration_max_ms": { "type": "number", "minimum": 0, "description": "Maximum execution duration in milliseconds." }, "has_errors": { "type": "string", "enum": [ "true", "false" ], "description": "Filter by whether the execution has errors; transformed to boolean by validation." }, "trigger_type": { "type": "string", "enum": [ "time_based", "event_based", "condition_based", "manual", "recurring", "webhook" ], "description": "Filter by automation trigger type." } } }, "AutomationRulesListQuery": { "type": "object", "properties": { "page": { "type": "string", "description": "Page number as a query string. Defaults to 1." }, "limit": { "type": "string", "description": "Page size as a query string. Must parse to 1 through 100; defaults to 25." }, "sort": { "type": "string", "description": "Accepted by shared list query validation; service currently orders rules by created_at desc." }, "order": { "type": "string", "enum": [ "asc", "desc" ], "description": "Accepted by shared list query validation." }, "search": { "type": "string", "description": "Accepted by shared filter validation." }, "created_from": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "created_to": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "updated_from": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "updated_to": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "is_active": { "type": "string", "enum": [ "true", "false" ], "description": "Accepted by shared filter validation and transformed to boolean." }, "name": { "type": "string", "description": "Optional rule name filter." }, "status": { "type": "string", "enum": [ "active", "inactive", "draft", "error" ], "description": "Filter by automation rule status." }, "trigger_type": { "type": "string", "enum": [ "time_based", "event_based", "condition_based", "manual", "recurring", "webhook" ], "description": "Filter by trigger type." }, "priority": { "type": "string", "enum": [ "low", "normal", "high", "critical" ], "description": "Filter by priority level." }, "template_category": { "type": "string", "description": "Filter template category." }, "created_by": { "type": "string", "format": "uuid", "description": "Filter by creator user UUID from automation_rules.created_by." }, "last_executed_from": { "type": "string", "format": "date-time", "description": "Filter for rules last executed at or after timestamp." }, "last_executed_to": { "type": "string", "format": "date-time", "description": "Filter for rules last executed at or before timestamp." }, "execution_count_min": { "type": "integer", "minimum": 0, "description": "Minimum execution count filter." }, "execution_count_max": { "type": "integer", "minimum": 0, "description": "Maximum execution count filter." } } }, "AutomationTemplatesListQuery": { "type": "object", "properties": { "page": { "type": "string", "description": "Page number as a query string. Defaults to 1." }, "limit": { "type": "string", "description": "Page size as a query string. Must parse to 1 through 100; defaults to 25." }, "sort": { "type": "string", "description": "Accepted by shared list query validation; service currently orders templates by created_at desc." }, "order": { "type": "string", "enum": [ "asc", "desc" ], "description": "Accepted by shared list query validation." }, "search": { "type": "string", "description": "Accepted by shared filter validation." }, "created_from": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "created_to": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "updated_from": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "updated_to": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "is_active": { "type": "string", "enum": [ "true", "false" ], "description": "Accepted by shared filter validation and transformed to boolean." }, "category": { "type": "string", "description": "Filter templates by category." }, "compatible_version": { "type": "string", "description": "Filter templates by compatibility version tag." }, "author": { "type": "string", "description": "Filter templates by author/user id value saved on the template." }, "usage_count_min": { "type": "integer", "minimum": 0, "description": "Minimum usage_count filter." }, "usage_count_max": { "type": "integer", "minimum": 0, "description": "Maximum usage_count filter." } } }, "AutomationPerformanceQuery": { "type": "object", "properties": { "date_from": { "type": "string", "format": "date-time", "description": "Optional lower bound for performance window." }, "date_to": { "type": "string", "format": "date-time", "description": "Optional upper bound for performance window." }, "group_by": { "type": "string", "enum": [ "hour", "day", "week", "month" ], "description": "Aggregation bucket size; defaults to day." }, "rule_ids": { "type": "string", "description": "Raw query value copied from URL. The controller expects an array UUID schema but validateQueryParams passes string values, so array filters currently require custom caller-side encoding or fail validation." }, "metrics": { "type": "string", "description": "Raw query value copied from URL. The controller expects an array enum schema but validateQueryParams passes string values, so array filters currently require custom caller-side encoding or fail validation." } } }, "AutomationExecutionLogEntry": { "type": "object", "properties": { "timestamp": { "type": "string", "format": "date-time", "description": "Log timestamp." }, "level": { "type": "string", "enum": [ "debug", "info", "warn", "error" ], "description": "Log level." }, "message": { "type": "string", "description": "Log message." }, "action_index": { "type": "integer", "description": "Optional action index that produced the log." }, "metadata": { "type": "object", "additionalProperties": {}, "description": "Optional structured log metadata." } }, "required": [ "level", "message" ] }, "AutomationLinks": { "type": "object", "additionalProperties": {}, "description": "HATEOAS links generated by the automation controller. Some generic links may point to methods not implemented by a route file." }, "AutomationExecution": { "type": "object", "properties": { "execution_id": { "type": "string", "format": "uuid", "description": "Execution UUID generated with crypto.randomUUID when the execution is created." }, "automation_rule_id": { "type": "string", "format": "uuid", "description": "Parent automation rule UUID." }, "trigger_data": { "type": "object", "additionalProperties": {}, "description": "Trigger payload captured when execution started." }, "status": { "type": "string", "enum": [ "pending", "running", "completed", "failed", "cancelled", "timeout", "skipped" ], "description": "Current execution status." }, "started_at": { "type": "string", "format": "date-time", "description": "Timestamp when execution started." }, "completed_at": { "type": [ "string", "null" ], "format": "date-time", "description": "Timestamp when execution completed." }, "duration_ms": { "type": [ "number", "null" ], "minimum": 0, "description": "Execution duration in milliseconds." }, "actions_total": { "type": "integer", "minimum": 0, "description": "Number of actions planned for this execution." }, "actions_successful": { "type": "integer", "minimum": 0, "description": "Number of actions completed successfully." }, "actions_failed": { "type": "integer", "minimum": 0, "description": "Number of actions that failed." }, "error_message": { "type": [ "string", "null" ], "description": "Top-level error message, when failed." }, "error_stack": { "type": [ "string", "null" ], "description": "Error stack, when captured." }, "failed_action_index": { "type": [ "integer", "null" ], "description": "Index of failed action, when applicable." }, "execution_context": { "type": "object", "additionalProperties": {}, "description": "Execution context data." }, "logs": { "type": "array", "items": { "$ref": "#/components/schemas/AutomationExecutionLogEntry" }, "description": "Execution log entries." }, "tenant": { "type": "string", "format": "uuid", "description": "Tenant UUID scoped from the API key context." }, "_links": { "$ref": "#/components/schemas/AutomationLinks" } }, "required": [ "execution_id", "automation_rule_id", "status", "actions_total", "actions_successful", "actions_failed", "tenant" ] }, "AutomationPagination": { "type": "object", "properties": { "page": { "type": "integer", "description": "Current page number." }, "limit": { "type": "integer", "description": "Page size." }, "total": { "type": "integer", "description": "Total matching executions." }, "totalPages": { "type": "integer", "description": "Total page count." }, "hasNext": { "type": "boolean", "description": "Whether another page exists." }, "hasPrev": { "type": "boolean", "description": "Whether a previous page exists." } }, "required": [ "page", "limit", "total", "totalPages", "hasNext", "hasPrev" ] }, "AutomationExecutionListResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/AutomationExecution" }, "description": "Automation execution rows. Current service code wraps the DB result array in another array; this schema documents the intended flat shape returned by createPaginatedResponse." }, "pagination": { "$ref": "#/components/schemas/AutomationPagination" }, "meta": { "type": "object", "properties": { "filters": { "type": "object", "additionalProperties": {}, "description": "Validated filters echoed by the controller." }, "resource": { "type": "string", "enum": [ "automation_execution" ], "description": "Resource name supplied by the controller." } } } }, "required": [ "data", "pagination" ] }, "AutomationExecutionResponse": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/AutomationExecution" } }, "required": [ "data" ] }, "AutomationErrorResponse": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string", "description": "Machine-readable error code, such as UNAUTHORIZED, FORBIDDEN, VALIDATION_ERROR, or INTERNAL_ERROR." }, "message": { "type": "string", "description": "Human-readable error message." }, "details": { "description": "Optional structured error details." } }, "required": [ "code", "message" ] } }, "required": [ "error" ] }, "AutomationMetaResponse": { "type": "object", "properties": { "data": { "type": "object", "properties": { "trigger_types": { "type": "array", "items": { "type": "string", "enum": [ "time_based", "event_based", "condition_based", "manual", "recurring", "webhook" ] }, "description": "Valid automation trigger types." }, "action_types": { "type": "array", "items": { "type": "string", "enum": [ "email_notification", "sms_notification", "webhook_call", "database_update", "ticket_creation", "ticket_update", "project_update", "time_entry_creation", "invoice_generation", "custom_script", "workflow_execution", "system_command" ] }, "description": "Valid automation action types." }, "rule_statuses": { "type": "array", "items": { "type": "string", "enum": [ "active", "inactive", "draft", "error" ] }, "description": "Valid automation rule statuses." }, "execution_statuses": { "type": "array", "items": { "type": "string", "enum": [ "pending", "running", "completed", "failed", "cancelled", "timeout", "skipped" ] }, "description": "Valid automation execution statuses." }, "priority_levels": { "type": "array", "items": { "type": "string", "enum": [ "low", "normal", "high", "critical" ] }, "description": "Valid automation priority levels." }, "condition_operators": { "type": "array", "items": { "type": "string", "enum": [ "equals", "not_equals", "greater_than", "less_than", "greater_than_or_equal", "less_than_or_equal", "contains", "not_contains", "starts_with", "ends_with", "in", "not_in", "exists", "not_exists", "regex_match" ] }, "description": "Valid condition operators." } }, "required": [ "trigger_types", "action_types", "rule_statuses", "execution_statuses", "priority_levels", "condition_operators" ] } }, "required": [ "data" ] }, "AutomationRuleCondition": { "type": "object", "properties": { "field": { "type": "string", "description": "Field path evaluated for this condition." }, "operator": { "type": "string", "enum": [ "equals", "not_equals", "greater_than", "less_than", "greater_than_or_equal", "less_than_or_equal", "contains", "not_contains", "starts_with", "ends_with", "in", "not_in", "exists", "not_exists", "regex_match" ] }, "value": { "description": "Condition comparison value." }, "data_type": { "type": "string", "enum": [ "string", "number", "boolean", "date", "array", "object" ] } }, "required": [ "field", "operator" ] }, "AutomationRuleAction": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "email_notification", "sms_notification", "webhook_call", "database_update", "ticket_creation", "ticket_update", "project_update", "time_entry_creation", "invoice_generation", "custom_script", "workflow_execution", "system_command" ], "description": "Automation action type." }, "config": { "type": "object", "additionalProperties": {}, "description": "Action configuration object. Expected keys depend on action type." }, "order": { "type": "integer", "minimum": 1, "description": "Execution order (1-based)." }, "continue_on_error": { "type": "boolean", "description": "Continue execution after a failed action." }, "timeout_seconds": { "type": "integer", "minimum": 1, "description": "Optional per-action timeout in seconds." }, "retry_attempts": { "type": "integer", "minimum": 0, "maximum": 5, "description": "Optional retry count for this action." } }, "required": [ "type", "config", "order" ] }, "AutomationNotificationSettings": { "type": "object", "properties": { "notify_on_success": { "type": "boolean" }, "notify_on_failure": { "type": "boolean" }, "notification_emails": { "type": "array", "items": { "type": "string", "format": "email" } }, "notification_webhooks": { "type": "array", "items": { "type": "string", "format": "uri" } } } }, "AutomationRule": { "type": "object", "properties": { "rule_id": { "type": "string", "format": "uuid", "description": "Rule UUID generated with crypto.randomUUID when a rule is created." }, "tenant": { "type": "string", "format": "uuid", "description": "Tenant UUID scoped from API key authentication." }, "name": { "type": "string", "description": "Rule display name." }, "description": { "type": [ "string", "null" ] }, "status": { "type": "string", "enum": [ "active", "inactive", "draft", "error" ], "description": "Current automation rule status." }, "priority": { "type": "string", "enum": [ "low", "normal", "high", "critical" ], "description": "Current automation rule priority." }, "trigger_type": { "type": "string", "enum": [ "time_based", "event_based", "condition_based", "manual", "recurring", "webhook" ], "description": "Trigger type used to start rule execution." }, "trigger_config": { "type": "object", "additionalProperties": {}, "description": "Trigger configuration object stored with the rule." }, "conditions": { "type": "array", "items": { "$ref": "#/components/schemas/AutomationRuleCondition" }, "description": "Optional additional rule conditions." }, "condition_logic": { "type": "string", "enum": [ "and", "or" ], "description": "Condition combination logic." }, "actions": { "type": "array", "items": { "$ref": "#/components/schemas/AutomationRuleAction" }, "description": "Ordered action list for this rule." }, "max_concurrent_executions": { "type": "integer", "minimum": 1 }, "execution_timeout_minutes": { "type": "integer", "minimum": 1 }, "retry_failed_executions": { "type": "boolean" }, "is_template": { "type": "boolean" }, "template_category": { "type": [ "string", "null" ] }, "tags": { "type": "array", "items": { "type": "string" } }, "metadata": { "type": "object", "additionalProperties": {} }, "notification_settings": { "$ref": "#/components/schemas/AutomationNotificationSettings" }, "created_by": { "type": [ "string", "null" ], "format": "uuid", "description": "User UUID from API key context that created the rule." }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" }, "execution_count": { "type": "integer", "minimum": 0 }, "last_executed": { "type": [ "string", "null" ], "format": "date-time" }, "execution_statistics": { "type": "object", "additionalProperties": {}, "description": "Included on GET /rules/{id} by service lookup helper." }, "_links": { "$ref": "#/components/schemas/AutomationLinks" } }, "required": [ "rule_id", "tenant", "name", "status", "priority", "trigger_type", "trigger_config", "actions" ] }, "CreateAutomationRuleRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 255 }, "description": { "type": "string" }, "status": { "type": "string", "enum": [ "active", "inactive", "draft", "error" ], "description": "Defaults to draft when omitted." }, "priority": { "type": "string", "enum": [ "low", "normal", "high", "critical" ], "description": "Defaults to normal when omitted." }, "trigger_type": { "type": "string", "enum": [ "time_based", "event_based", "condition_based", "manual", "recurring", "webhook" ] }, "trigger_config": { "type": "object", "additionalProperties": {}, "description": "Trigger-type-specific configuration object." }, "conditions": { "type": "array", "items": { "$ref": "#/components/schemas/AutomationRuleCondition" } }, "condition_logic": { "type": "string", "enum": [ "and", "or" ] }, "actions": { "type": "array", "items": { "$ref": "#/components/schemas/AutomationRuleAction" }, "minItems": 1 }, "max_concurrent_executions": { "type": "integer", "minimum": 1 }, "execution_timeout_minutes": { "type": "integer", "minimum": 1 }, "retry_failed_executions": { "type": "boolean" }, "is_template": { "type": "boolean" }, "template_category": { "type": "string" }, "tags": { "type": "array", "items": { "type": "string" } }, "metadata": { "type": "object", "additionalProperties": {} }, "notification_settings": { "$ref": "#/components/schemas/AutomationNotificationSettings" } }, "required": [ "name", "trigger_type", "trigger_config", "actions" ] }, "UpdateAutomationRuleRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 255 }, "description": { "type": "string" }, "status": { "type": "string", "enum": [ "active", "inactive", "draft", "error" ], "description": "Defaults to draft when omitted." }, "priority": { "type": "string", "enum": [ "low", "normal", "high", "critical" ], "description": "Defaults to normal when omitted." }, "trigger_type": { "type": "string", "enum": [ "time_based", "event_based", "condition_based", "manual", "recurring", "webhook" ] }, "trigger_config": { "type": "object", "additionalProperties": {}, "description": "Trigger-type-specific configuration object." }, "conditions": { "type": "array", "items": { "$ref": "#/components/schemas/AutomationRuleCondition" } }, "condition_logic": { "type": "string", "enum": [ "and", "or" ] }, "actions": { "type": "array", "items": { "$ref": "#/components/schemas/AutomationRuleAction" }, "minItems": 1 }, "max_concurrent_executions": { "type": "integer", "minimum": 1 }, "execution_timeout_minutes": { "type": "integer", "minimum": 1 }, "retry_failed_executions": { "type": "boolean" }, "is_template": { "type": "boolean" }, "template_category": { "type": "string" }, "tags": { "type": "array", "items": { "type": "string" } }, "metadata": { "type": "object", "additionalProperties": {} }, "notification_settings": { "$ref": "#/components/schemas/AutomationNotificationSettings" } } }, "ManualExecutionRequest": { "type": "object", "properties": { "automation_rule_id": { "type": "string", "format": "uuid", "description": "Required by current validation schema but ignored by controller/service, which execute the path rule ID." }, "execution_data": { "type": "object", "additionalProperties": {} }, "override_conditions": { "type": "boolean", "description": "Set true to allow execution of inactive rules." }, "dry_run": { "type": "boolean", "description": "When true, returns a completed execution without queueing actions." } }, "required": [ "automation_rule_id" ] }, "BulkStatusUpdateRequest": { "type": "object", "properties": { "ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1, "maxItems": 100, "description": "Rule UUIDs from automation_rules.rule_id." }, "status": { "type": "string", "enum": [ "active", "inactive", "draft", "error" ], "description": "Status to apply to each provided rule id." } }, "required": [ "ids", "status" ] }, "BulkExecuteRequest": { "type": "object", "properties": { "automation_rule_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1, "maxItems": 20, "description": "Rule UUIDs to execute." }, "execution_data": { "type": "object", "additionalProperties": {}, "description": "Optional shared execution payload for each run." }, "override_conditions": { "type": "boolean", "description": "Allow execution of inactive rules when true." }, "sequential_execution": { "type": "boolean", "description": "When true, executes rules sequentially; otherwise Promise.all is used." } }, "required": [ "automation_rule_ids" ] }, "AutomationTemplateVariable": { "type": "object", "properties": { "name": { "type": "string" }, "display_name": { "type": "string" }, "description": { "type": "string" }, "type": { "type": "string", "enum": [ "string", "number", "boolean", "date", "select", "multiselect" ] }, "required": { "type": "boolean" }, "default_value": {}, "options": { "type": "array", "items": { "type": "object", "properties": { "label": { "type": "string" }, "value": {} }, "required": [ "label" ] } }, "validation": { "type": "object", "properties": { "min": { "type": "number" }, "max": { "type": "number" }, "pattern": { "type": "string" }, "custom_validator": { "type": "string" } } } }, "required": [ "name", "type" ] }, "AutomationTemplate": { "type": "object", "properties": { "template_id": { "type": "string", "format": "uuid", "description": "Template UUID generated with crypto.randomUUID." }, "name": { "type": "string" }, "description": { "type": [ "string", "null" ] }, "category": { "type": "string" }, "icon": { "type": [ "string", "null" ] }, "template_config": { "type": "object", "additionalProperties": {}, "description": "Template rule blueprint derived from an automation rule." }, "version": { "type": "string" }, "author": { "type": [ "string", "null" ] }, "compatible_versions": { "type": "array", "items": { "type": "string" } }, "required_permissions": { "type": "array", "items": { "type": "string" } }, "usage_count": { "type": "integer", "minimum": 0 }, "last_used": { "type": [ "string", "null" ], "format": "date-time" }, "template_variables": { "type": "array", "items": { "$ref": "#/components/schemas/AutomationTemplateVariable" } }, "is_active": { "type": "boolean" }, "is_featured": { "type": "boolean" }, "tenant": { "type": "string", "format": "uuid" }, "_links": { "$ref": "#/components/schemas/AutomationLinks" } }, "required": [ "template_id", "name", "category", "template_config", "tenant" ] }, "CreateAutomationTemplateRequest": { "type": "object", "properties": { "automation_rule_id": { "type": "string", "format": "uuid", "description": "Source automation_rules.rule_id copied into template_config." }, "template_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "template_description": { "type": "string" }, "category": { "type": "string", "minLength": 1 }, "template_variables": { "type": "array", "items": { "type": "object", "properties": { "rule_field_path": { "type": "string", "minLength": 1 }, "variable_name": { "type": "string", "minLength": 1 }, "display_name": { "type": "string", "minLength": 1 }, "description": { "type": "string" }, "type": { "type": "string", "enum": [ "string", "number", "boolean", "date", "select", "multiselect" ] }, "required": { "type": "boolean" } }, "required": [ "rule_field_path", "variable_name", "display_name", "type" ] } } }, "required": [ "automation_rule_id", "template_name", "category" ] }, "UseAutomationTemplateRequest": { "type": "object", "properties": { "variables": { "type": "object", "additionalProperties": {}, "description": "Optional variable substitutions applied to template_config before creating a new automation rule." } } }, "AutomationRuleResponse": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/AutomationRule" } }, "required": [ "data" ] }, "AutomationRuleListResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/AutomationRule" } }, "pagination": { "$ref": "#/components/schemas/AutomationPagination" }, "meta": { "type": "object", "properties": { "filters": { "type": "object", "additionalProperties": {} }, "resource": { "type": "string", "enum": [ "automation_rule" ] } } } }, "required": [ "data", "pagination" ] }, "AutomationTemplateResponse": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/AutomationTemplate" } }, "required": [ "data" ] }, "AutomationTemplateListResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/AutomationTemplate" }, "description": "Template rows. Current service code wraps the DB result array in another array before createPaginatedResponse, which can produce nested data in practice." }, "pagination": { "$ref": "#/components/schemas/AutomationPagination" }, "meta": { "type": "object", "properties": { "filters": { "type": "object", "additionalProperties": {} }, "resource": { "type": "string", "enum": [ "automation_template" ] } } } }, "required": [ "data", "pagination" ] }, "AutomationStatisticsResponse": { "type": "object", "properties": { "data": { "type": "object", "properties": { "total_rules": { "type": "integer", "minimum": 0 }, "active_rules": { "type": "integer", "minimum": 0 }, "inactive_rules": { "type": "integer", "minimum": 0 }, "draft_rules": { "type": "integer", "minimum": 0 }, "error_rules": { "type": "integer", "minimum": 0 }, "total_executions": { "type": "integer", "minimum": 0 }, "successful_executions": { "type": "integer", "minimum": 0 }, "failed_executions": { "type": "integer", "minimum": 0 }, "executions_today": { "type": "integer", "minimum": 0 }, "executions_this_week": { "type": "integer", "minimum": 0 }, "executions_this_month": { "type": "integer", "minimum": 0 }, "avg_execution_time_ms": { "type": "number", "minimum": 0 }, "success_rate_percent": { "type": "number", "minimum": 0, "maximum": 100 }, "_links": { "type": "object", "properties": { "self": { "type": "string", "enum": [ "/api/v1/automation/statistics" ] }, "rules": { "type": "string", "enum": [ "/api/v1/automation/rules" ] }, "executions": { "type": "string", "enum": [ "/api/v1/automation/executions" ] }, "templates": { "type": "string", "enum": [ "/api/v1/automation/templates" ] }, "performance": { "type": "string", "enum": [ "/api/v1/automation/performance" ] } }, "required": [ "self", "rules", "executions", "templates", "performance" ] } }, "required": [ "total_rules", "active_rules", "inactive_rules", "draft_rules", "error_rules", "total_executions", "successful_executions", "failed_executions", "executions_today", "executions_this_week", "executions_this_month" ] } }, "required": [ "data" ] }, "AutomationPerformanceResponse": { "type": "object", "properties": { "data": { "allOf": [ { "type": "object", "additionalProperties": {} }, { "type": "object", "properties": { "_links": { "type": "object", "properties": { "self": { "type": "string", "enum": [ "/api/v1/automation/performance" ] }, "statistics": { "type": "string", "enum": [ "/api/v1/automation/statistics" ] } }, "required": [ "self", "statistics" ] } } } ], "description": "Performance payload from AutomationService.calculatePerformanceMetrics plus links." } }, "required": [ "data" ] }, "BulkStatusUpdateResponse": { "type": "object", "properties": { "data": { "type": "object", "properties": { "action": { "type": "string", "enum": [ "bulk_status_update" ] }, "status": { "type": "string", "enum": [ "active", "inactive", "draft", "error" ] }, "rule_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "updated": { "type": "integer", "minimum": 0 }, "errors": { "type": "array", "items": { "type": "string" }, "description": "Per-rule failures emitted as `: ` entries." }, "message": { "type": "string" } }, "required": [ "action", "status", "rule_ids", "updated", "errors", "message" ] } }, "required": [ "data" ] }, "BulkExecuteResponse": { "type": "object", "properties": { "data": { "type": "object", "properties": { "action": { "type": "string", "enum": [ "bulk_execute" ] }, "rule_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "started": { "type": "integer", "minimum": 0 }, "errors": { "type": "array", "items": { "type": "string" }, "description": "Per-rule failures emitted as `: ` entries." }, "sequential": { "type": "boolean" }, "message": { "type": "string" } }, "required": [ "action", "rule_ids", "started", "errors", "message" ] } }, "required": [ "data" ] }, "LicenceCountHeaders": { "type": "object", "properties": { "x-webhook-signature": { "type": "string", "description": "HMAC-SHA256 hex digest proving the caller knows ALGA_WEBHOOK_SECRET." } }, "required": [ "x-webhook-signature" ] }, "LicenceCountQuery": { "type": "object", "properties": { "tenant_id": { "type": "string", "description": "Tenant identifier to query. This must match a tenants.tenant record." } }, "required": [ "tenant_id" ] }, "LicenceUsageResponse": { "type": "object", "properties": { "tenant_id": { "type": "string", "description": "Echoed tenant identifier from the request." }, "limit": { "type": [ "integer", "null" ], "description": "Licensed internal-user count from tenants.licensed_user_count. Null means unlimited." }, "used": { "type": "integer", "description": "Count of active internal users where users.user_type is internal and is_inactive is false." }, "remaining": { "type": [ "integer", "null" ], "description": "Calculated remaining seats as max(0, limit - used), or null when limit is unlimited." }, "last_updated": { "type": [ "string", "null" ], "description": "ISO timestamp from tenants.last_license_update, or null if no license update has been recorded." } }, "required": [ "tenant_id", "limit", "used", "remaining", "last_updated" ] }, "LicenceCountUpdateRequest": { "type": "object", "properties": { "tenant_id": { "type": "string", "description": "Tenant identifier from tenants.tenant whose licensed user count should be updated." }, "license_count": { "type": "integer", "minimum": 0, "description": "Licensed internal-user count from the Stripe subscription quantity." }, "event_id": { "type": "string", "description": "Stripe webhook event ID. When supplied, the handler uses tenants.stripe_event_id for idempotency." } }, "required": [ "tenant_id", "license_count" ] }, "LicenceCountUpdateResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Indicates the license count update was accepted." }, "tenant_id": { "type": "string", "description": "Echoed tenant identifier that was updated." }, "license_count": { "type": "integer", "description": "Echoed licensed user count that was written." }, "updated_at": { "type": "string", "format": "date-time", "description": "ISO timestamp generated by the handler for this response." } }, "required": [ "success", "tenant_id", "license_count", "updated_at" ] }, "LicenceCountErrorResponse": { "type": "object", "properties": { "error": { "type": "string", "description": "Human-readable error message." } }, "required": [ "error" ] }, "ChatMessage": { "type": "object", "properties": { "role": { "type": "string", "enum": [ "user", "assistant" ], "description": "Role of the message author in the conversation." }, "content": { "type": "string", "description": "Message text sent to the chat model." } }, "required": [ "role", "content" ] }, "ChatStreamRequest": { "type": "object", "properties": { "inputs": { "type": "array", "items": { "$ref": "#/components/schemas/ChatMessage" }, "minItems": 1, "description": "Conversation messages in order. These are passed to the OpenRouter chat model." }, "options": { "type": "object", "additionalProperties": {}, "description": "Optional model options. Currently declared by the internal interface but not consumed by the handler." }, "model": { "type": "string", "description": "Optional model override. Currently ignored; the server uses OPENROUTER_CHAT_MODEL or minimax/minimax-m2." }, "meta": { "type": "object", "properties": { "authorization": { "type": "string", "description": "Optional downstream authorization token. Currently declared but not consumed by the handler." } }, "description": "Optional metadata for legacy chat clients." } }, "required": [ "inputs" ] }, "ChatTitleStreamRequest": { "type": "object", "properties": { "inputs": { "type": "array", "items": { "$ref": "#/components/schemas/ChatMessage" }, "minItems": 1, "description": "Conversation messages used as context for generating a short chat title." } }, "required": [ "inputs" ] }, "ChatStreamEvent": { "type": "object", "properties": { "content": { "type": "string", "description": "SSE data payload content. Normal events contain generated text; terminal events may contain [DONE]." }, "type": { "type": "string", "enum": [ "text", "error" ], "description": "Event type. text carries model output; error carries an in-stream error message." } }, "required": [ "content" ] }, "ChatStreamError": { "type": "object", "properties": { "error": { "type": "string", "description": "Human-readable error message." } }, "required": [ "error" ] }, "ChatStreamSlugParams": { "type": "object", "properties": { "slug": { "type": "string", "description": "Catch-all chat stream path segment. The current handler accepts the value but does not use it." } }, "required": [ "slug" ] }, "ClientIdParam": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" } }, "required": [ "id" ] }, "ContactIdParam": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" } }, "required": [ "id" ] }, "ClientContractLineIdParam": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" } }, "required": [ "id" ] }, "ClientListQuery": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] }, "search": { "type": "string" }, "created_from": { "type": "string", "format": "date-time" }, "created_to": { "type": "string", "format": "date-time" }, "updated_from": { "type": "string", "format": "date-time" }, "updated_to": { "type": "string", "format": "date-time" }, "client_name": { "type": "string" }, "email": { "type": "string" }, "client_type": { "type": "string" }, "billing_cycle": { "type": "string", "enum": [ "weekly", "bi-weekly", "monthly", "quarterly", "semi-annually", "annually" ] }, "is_inactive": { "type": "string", "enum": [ "true", "false" ] }, "is_tax_exempt": { "type": "string", "enum": [ "true", "false" ] }, "account_manager_id": { "type": "string", "format": "uuid" }, "region_code": { "type": "string" }, "credit_balance_min": { "type": "string" }, "credit_balance_max": { "type": "string" }, "has_credit_limit": { "type": "string", "enum": [ "true", "false" ] }, "industry": { "type": "string" }, "company_size": { "type": "string" } } }, "ClientBody": { "type": "object", "properties": { "client_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "phone_no": { "type": "string" }, "email": { "type": "string", "format": "email" }, "url": { "type": "string", "format": "uri" }, "address": { "type": "string" }, "client_type": { "type": "string" }, "tax_id_number": { "type": "string" }, "notes": { "type": "string" }, "properties": { "type": "object", "additionalProperties": {} }, "payment_terms": { "type": "string" }, "billing_cycle": { "type": "string", "enum": [ "weekly", "bi-weekly", "monthly", "quarterly", "semi-annually", "annually" ] }, "credit_limit": { "type": "number", "minimum": 0 }, "preferred_payment_method": { "type": "string" }, "auto_invoice": { "type": "boolean" }, "invoice_delivery_method": { "type": "string", "enum": [ "email", "mail", "portal" ] }, "region_code": { "type": "string" }, "is_tax_exempt": { "type": "boolean" }, "tax_exemption_certificate": { "type": "string" }, "timezone": { "type": "string" }, "invoice_template_id": { "type": "string", "format": "uuid" }, "billing_contact_id": { "type": "string", "format": "uuid" }, "billing_email": { "type": "string", "format": "email" }, "account_manager_id": { "type": "string", "format": "uuid" }, "is_inactive": { "type": "boolean" }, "tags": { "type": "array", "items": { "type": "string" } } }, "required": [ "client_name", "billing_cycle" ] }, "ClientLocationBody": { "type": "object", "properties": { "location_name": { "type": "string" }, "address_line1": { "type": "string", "minLength": 1 }, "address_line2": { "type": "string" }, "address_line3": { "type": "string" }, "city": { "type": "string", "minLength": 1 }, "state_province": { "type": "string" }, "postal_code": { "type": "string" }, "country_code": { "type": "string", "minLength": 2, "maxLength": 3 }, "country_name": { "type": "string", "minLength": 1 }, "region_code": { "type": "string" }, "is_billing_address": { "type": "boolean" }, "is_shipping_address": { "type": "boolean" }, "is_default": { "type": "boolean" }, "phone": { "type": "string" }, "fax": { "type": "string" }, "email": { "type": "string", "format": "email" }, "notes": { "type": "string" }, "is_active": { "type": "boolean" } }, "required": [ "address_line1", "city", "country_code", "country_name" ] }, "ContactListQuery": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] }, "search": { "type": "string" }, "created_from": { "type": "string", "format": "date-time" }, "created_to": { "type": "string", "format": "date-time" }, "updated_from": { "type": "string", "format": "date-time" }, "updated_to": { "type": "string", "format": "date-time" }, "full_name": { "type": "string" }, "email": { "type": "string" }, "phone_number": { "type": "string" }, "client_id": { "type": "string", "format": "uuid" }, "role": { "type": "string" }, "is_inactive": { "type": "string", "enum": [ "true", "false" ] }, "has_client": { "type": "string", "enum": [ "true", "false" ] }, "client_name": { "type": "string" } } }, "ContactBody": { "type": "object", "properties": { "full_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "client_id": { "type": "string", "format": "uuid" }, "phone_numbers": { "type": "array", "items": { "type": "object", "additionalProperties": {} } }, "email": { "type": "string", "format": "email" }, "primary_email_canonical_type": { "type": [ "string", "null" ] }, "primary_email_custom_type": { "type": [ "string", "null" ] }, "primary_email_custom_type_id": { "type": [ "string", "null" ], "format": "uuid" }, "additional_email_addresses": { "type": "array", "items": { "type": "object", "additionalProperties": {} } }, "role": { "type": "string" }, "notes": { "type": "string" }, "is_inactive": { "type": "boolean" }, "tags": { "type": "array", "items": { "type": "string" } } }, "required": [ "full_name", "email" ] }, "ContactSearchQuery": { "type": "object", "properties": { "query": { "type": "string", "minLength": 1 }, "fields": { "type": "string", "description": "Comma-separated field list; parsed by contactSearchSchema transform." }, "client_id": { "type": "string", "format": "uuid" }, "include_inactive": { "type": "string", "enum": [ "true", "false" ] }, "limit": { "type": "string" } }, "required": [ "query" ] }, "ContactExportQuery": { "type": "object", "properties": { "format": { "type": "string", "enum": [ "csv", "json" ] }, "include_inactive": { "type": "string", "enum": [ "true", "false" ] }, "client_id": { "type": "string", "format": "uuid" }, "fields": { "type": "string", "description": "Array schema expects parsed list, but URL values arrive as string." } } }, "ClientContractLineQuery": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] }, "search": { "type": "string" }, "created_from": { "type": "string", "format": "date-time" }, "created_to": { "type": "string", "format": "date-time" }, "updated_from": { "type": "string", "format": "date-time" }, "updated_to": { "type": "string", "format": "date-time" }, "client_id": { "type": "string", "format": "uuid" }, "contract_line_id": { "type": "string", "format": "uuid" }, "service_category": { "type": "string" }, "is_active": { "type": "string", "enum": [ "true", "false" ] }, "has_custom_rate": { "type": "string", "enum": [ "true", "false" ] }, "is_contractd": { "type": "string", "enum": [ "true", "false" ] }, "start_date_from": { "type": "string", "format": "date-time" }, "start_date_to": { "type": "string", "format": "date-time" }, "end_date_from": { "type": "string", "format": "date-time" }, "end_date_to": { "type": "string", "format": "date-time" } } }, "ClientContractLineBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "contract_line_id": { "type": "string", "format": "uuid" }, "service_category": { "type": "string" }, "start_date": { "type": "string", "format": "date-time" }, "end_date": { "type": "string", "format": "date-time" }, "is_active": { "type": "boolean" }, "custom_rate": { "type": "number", "minimum": 0 }, "client_contract_id": { "type": "string", "format": "uuid" } }, "required": [ "client_id", "contract_line_id", "start_date" ] }, "ClientContactApiError": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] }, "ContactResource": { "type": "object", "properties": { "contact_name_id": { "type": "string", "format": "uuid" }, "full_name": { "type": "string" }, "client_id": { "type": [ "string", "null" ], "format": "uuid" }, "email": { "type": "string", "format": "email" }, "role": { "type": [ "string", "null" ] }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" }, "is_inactive": { "type": "boolean" }, "tenant": { "type": "string", "format": "uuid" }, "phone_numbers": { "type": "array", "items": { "type": "object", "properties": { "contact_phone_number_id": { "type": "string", "format": "uuid" }, "phone_number": { "type": "string" }, "normalized_phone_number": { "type": "string" }, "canonical_type": { "type": [ "string", "null" ] }, "custom_phone_type_id": { "type": [ "string", "null" ], "format": "uuid" }, "custom_type": { "type": [ "string", "null" ] }, "is_default": { "type": "boolean" }, "display_order": { "type": "integer", "minimum": 0 } }, "required": [ "contact_phone_number_id", "phone_number", "normalized_phone_number", "custom_type", "is_default", "display_order" ] } }, "additional_email_addresses": { "type": "array", "items": { "type": "object", "properties": { "contact_additional_email_address_id": { "type": "string", "format": "uuid" }, "email_address": { "type": "string", "format": "email" }, "normalized_email_address": { "type": "string", "format": "email" }, "canonical_type": { "type": [ "string", "null" ] }, "custom_email_type_id": { "type": [ "string", "null" ], "format": "uuid" }, "custom_type": { "type": [ "string", "null" ] }, "display_order": { "type": "integer", "minimum": 0 } }, "required": [ "contact_additional_email_address_id", "email_address", "normalized_email_address", "custom_type", "display_order" ] } }, "default_phone_number": { "type": [ "string", "null" ] }, "default_phone_type": { "type": [ "string", "null" ] }, "primary_email_canonical_type": { "type": [ "string", "null" ] }, "primary_email_custom_type_id": { "type": [ "string", "null" ], "format": "uuid" }, "primary_email_type": { "type": [ "string", "null" ] }, "notes": { "type": [ "string", "null" ] }, "avatarUrl": { "type": [ "string", "null" ] }, "tags": { "type": "array", "items": { "type": "string" } }, "client_name": { "type": [ "string", "null" ] } }, "required": [ "contact_name_id", "full_name", "client_id", "email", "role", "created_at", "updated_at", "is_inactive", "tenant" ] }, "ContactStatsResource": { "type": "object", "properties": { "total_contacts": { "type": "number" }, "active_contacts": { "type": "number" }, "inactive_contacts": { "type": "number" }, "contacts_with_client": { "type": "number" }, "contacts_without_client": { "type": "number" }, "contacts_by_role": { "type": "object", "additionalProperties": { "type": "number" } }, "recent_contacts": { "type": "number" } }, "required": [ "total_contacts", "active_contacts", "inactive_contacts", "contacts_with_client", "contacts_without_client", "contacts_by_role", "recent_contacts" ] }, "ClientContractLineResource": { "type": "object", "properties": { "client_contract_line_id": { "type": "string", "format": "uuid" }, "client_id": { "type": "string", "format": "uuid" }, "contract_line_id": { "type": "string", "format": "uuid" }, "service_category": { "type": [ "string", "null" ] }, "start_date": { "type": "string", "format": "date-time" }, "end_date": { "type": [ "string", "null" ], "format": "date-time" }, "is_active": { "type": "boolean" }, "custom_rate": { "type": [ "number", "null" ] }, "client_contract_id": { "type": [ "string", "null" ], "format": "uuid" }, "tenant": { "type": "string", "format": "uuid" } }, "required": [ "client_contract_line_id", "client_id", "contract_line_id", "start_date", "is_active" ] }, "ContactExportRow": { "type": "object", "properties": { "contact_name_id": { "type": "string", "format": "uuid" }, "full_name": { "type": "string" }, "email": { "type": [ "string", "null" ], "format": "email" }, "role": { "type": [ "string", "null" ] }, "is_inactive": { "type": "boolean" }, "created_at": { "type": "string", "format": "date-time" }, "client_name": { "type": [ "string", "null" ] }, "default_phone_number": { "type": [ "string", "null" ] }, "default_phone_type": { "type": [ "string", "null" ] } }, "required": [ "contact_name_id", "full_name", "email", "role", "is_inactive", "created_at", "client_name", "default_phone_number", "default_phone_type" ] }, "ClientEnvelope": { "type": "object", "properties": { "data": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "client_name": { "type": "string" }, "phone_no": { "type": [ "string", "null" ] }, "credit_balance": { "type": "number" }, "email": { "type": [ "string", "null" ] }, "url": { "type": [ "string", "null" ] }, "address": { "type": [ "string", "null" ] }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" }, "is_inactive": { "type": "boolean" }, "client_type": { "type": [ "string", "null" ] }, "tax_id_number": { "type": [ "string", "null" ] }, "notes": { "type": [ "string", "null" ] }, "properties": { "type": "object", "properties": { "industry": { "type": "string" }, "company_size": { "type": "string" }, "annual_revenue": { "type": "string" }, "primary_contact_id": { "type": "string", "format": "uuid" }, "primary_contact_name": { "type": "string" }, "status": { "type": "string" }, "type": { "type": "string" }, "billing_address": { "type": "string" }, "tax_id": { "type": "string" }, "notes": { "type": "string" }, "payment_terms": { "type": "string" }, "website": { "type": "string", "format": "uri" }, "parent_client_id": { "type": "string", "format": "uuid" }, "parent_client_name": { "type": "string" }, "last_contact_date": { "type": "string", "format": "date-time" }, "logo": { "type": "string" } } }, "payment_terms": { "type": [ "string", "null" ] }, "billing_cycle": { "type": "string", "enum": [ "weekly", "bi-weekly", "monthly", "quarterly", "semi-annually", "annually" ] }, "credit_limit": { "type": [ "number", "null" ] }, "preferred_payment_method": { "type": [ "string", "null" ] }, "auto_invoice": { "type": "boolean" }, "invoice_delivery_method": { "type": [ "string", "null" ] }, "region_code": { "type": [ "string", "null" ] }, "is_tax_exempt": { "type": "boolean" }, "tax_exemption_certificate": { "type": [ "string", "null" ] }, "timezone": { "type": [ "string", "null" ] }, "invoice_template_id": { "type": [ "string", "null" ], "format": "uuid" }, "billing_contact_id": { "type": [ "string", "null" ], "format": "uuid" }, "billing_email": { "type": [ "string", "null" ] }, "account_manager_id": { "type": [ "string", "null" ], "format": "uuid" }, "account_manager_full_name": { "type": [ "string", "null" ] }, "logoUrl": { "type": [ "string", "null" ] }, "tenant": { "type": "string", "format": "uuid" }, "tags": { "type": "array", "items": { "type": "string" } } }, "required": [ "client_id", "client_name", "phone_no", "credit_balance", "email", "url", "address", "created_at", "updated_at", "is_inactive", "client_type", "tax_id_number", "notes", "payment_terms", "billing_cycle", "credit_limit", "preferred_payment_method", "auto_invoice", "invoice_delivery_method", "region_code", "is_tax_exempt", "tax_exemption_certificate", "timezone", "invoice_template_id", "billing_contact_id", "billing_email", "account_manager_id", "tenant" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "PaginatedClientEnvelope": { "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "client_name": { "type": "string" }, "phone_no": { "type": [ "string", "null" ] }, "credit_balance": { "type": "number" }, "email": { "type": [ "string", "null" ] }, "url": { "type": [ "string", "null" ] }, "address": { "type": [ "string", "null" ] }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" }, "is_inactive": { "type": "boolean" }, "client_type": { "type": [ "string", "null" ] }, "tax_id_number": { "type": [ "string", "null" ] }, "notes": { "type": [ "string", "null" ] }, "properties": { "type": "object", "properties": { "industry": { "type": "string" }, "company_size": { "type": "string" }, "annual_revenue": { "type": "string" }, "primary_contact_id": { "type": "string", "format": "uuid" }, "primary_contact_name": { "type": "string" }, "status": { "type": "string" }, "type": { "type": "string" }, "billing_address": { "type": "string" }, "tax_id": { "type": "string" }, "notes": { "type": "string" }, "payment_terms": { "type": "string" }, "website": { "type": "string", "format": "uri" }, "parent_client_id": { "type": "string", "format": "uuid" }, "parent_client_name": { "type": "string" }, "last_contact_date": { "type": "string", "format": "date-time" }, "logo": { "type": "string" } } }, "payment_terms": { "type": [ "string", "null" ] }, "billing_cycle": { "type": "string", "enum": [ "weekly", "bi-weekly", "monthly", "quarterly", "semi-annually", "annually" ] }, "credit_limit": { "type": [ "number", "null" ] }, "preferred_payment_method": { "type": [ "string", "null" ] }, "auto_invoice": { "type": "boolean" }, "invoice_delivery_method": { "type": [ "string", "null" ] }, "region_code": { "type": [ "string", "null" ] }, "is_tax_exempt": { "type": "boolean" }, "tax_exemption_certificate": { "type": [ "string", "null" ] }, "timezone": { "type": [ "string", "null" ] }, "invoice_template_id": { "type": [ "string", "null" ], "format": "uuid" }, "billing_contact_id": { "type": [ "string", "null" ], "format": "uuid" }, "billing_email": { "type": [ "string", "null" ] }, "account_manager_id": { "type": [ "string", "null" ], "format": "uuid" }, "account_manager_full_name": { "type": [ "string", "null" ] }, "logoUrl": { "type": [ "string", "null" ] }, "tenant": { "type": "string", "format": "uuid" }, "tags": { "type": "array", "items": { "type": "string" } } }, "required": [ "client_id", "client_name", "phone_no", "credit_balance", "email", "url", "address", "created_at", "updated_at", "is_inactive", "client_type", "tax_id_number", "notes", "payment_terms", "billing_cycle", "credit_limit", "preferred_payment_method", "auto_invoice", "invoice_delivery_method", "region_code", "is_tax_exempt", "tax_exemption_certificate", "timezone", "invoice_template_id", "billing_contact_id", "billing_email", "account_manager_id", "tenant" ] } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" }, "hasNext": { "type": "boolean" }, "hasPrev": { "type": "boolean" } }, "required": [ "page", "limit", "total", "totalPages", "hasNext", "hasPrev" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data", "pagination" ] }, "ClientStatsEnvelope": { "type": "object", "properties": { "data": { "type": "object", "properties": { "total_clients": { "type": "number" }, "active_clients": { "type": "number" }, "inactive_clients": { "type": "number" }, "clients_by_billing_cycle": { "type": "object", "additionalProperties": { "type": "number" } }, "clients_by_client_type": { "type": "object", "additionalProperties": { "type": "number" } }, "total_credit_balance": { "type": "number" }, "average_credit_balance": { "type": "number" } }, "required": [ "total_clients", "active_clients", "inactive_clients", "clients_by_billing_cycle", "clients_by_client_type", "total_credit_balance", "average_credit_balance" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "ClientContactEnvelope": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/ContactResource" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "PaginatedContactEnvelope": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/ContactResource" } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" }, "hasNext": { "type": "boolean" }, "hasPrev": { "type": "boolean" } }, "required": [ "page", "limit", "total", "totalPages", "hasNext", "hasPrev" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data", "pagination" ] }, "ContactSearchEnvelope": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/ContactResource" } }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "ContactStatsEnvelope": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/ContactStatsResource" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "ClientLocationsEnvelope": { "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object", "properties": { "location_id": { "type": "string", "format": "uuid" }, "client_id": { "type": "string", "format": "uuid" }, "location_name": { "type": [ "string", "null" ] }, "address_line1": { "type": "string" }, "address_line2": { "type": [ "string", "null" ] }, "address_line3": { "type": [ "string", "null" ] }, "city": { "type": "string" }, "state_province": { "type": [ "string", "null" ] }, "postal_code": { "type": [ "string", "null" ] }, "country_code": { "type": "string" }, "country_name": { "type": "string" }, "region_code": { "type": [ "string", "null" ] }, "is_billing_address": { "type": "boolean" }, "is_shipping_address": { "type": "boolean" }, "is_default": { "type": "boolean" }, "phone": { "type": [ "string", "null" ] }, "fax": { "type": [ "string", "null" ] }, "email": { "type": [ "string", "null" ] }, "notes": { "type": [ "string", "null" ] }, "is_active": { "type": "boolean" }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" }, "tenant": { "type": "string", "format": "uuid" } }, "required": [ "location_id", "client_id", "location_name", "address_line1", "address_line2", "address_line3", "city", "state_province", "postal_code", "country_code", "country_name", "region_code", "is_billing_address", "is_shipping_address", "is_default", "phone", "fax", "email", "notes", "is_active", "created_at", "updated_at", "tenant" ] } }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "ClientLocationEnvelope": { "type": "object", "properties": { "data": { "type": "object", "properties": { "location_id": { "type": "string", "format": "uuid" }, "client_id": { "type": "string", "format": "uuid" }, "location_name": { "type": [ "string", "null" ] }, "address_line1": { "type": "string" }, "address_line2": { "type": [ "string", "null" ] }, "address_line3": { "type": [ "string", "null" ] }, "city": { "type": "string" }, "state_province": { "type": [ "string", "null" ] }, "postal_code": { "type": [ "string", "null" ] }, "country_code": { "type": "string" }, "country_name": { "type": "string" }, "region_code": { "type": [ "string", "null" ] }, "is_billing_address": { "type": "boolean" }, "is_shipping_address": { "type": "boolean" }, "is_default": { "type": "boolean" }, "phone": { "type": [ "string", "null" ] }, "fax": { "type": [ "string", "null" ] }, "email": { "type": [ "string", "null" ] }, "notes": { "type": [ "string", "null" ] }, "is_active": { "type": "boolean" }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" }, "tenant": { "type": "string", "format": "uuid" } }, "required": [ "location_id", "client_id", "location_name", "address_line1", "address_line2", "address_line3", "city", "state_province", "postal_code", "country_code", "country_name", "region_code", "is_billing_address", "is_shipping_address", "is_default", "phone", "fax", "email", "notes", "is_active", "created_at", "updated_at", "tenant" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "ContactExportEnvelope": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/ContactExportRow" } }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "ClientContractLineListItem": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/ClientContractLineResource" } }, "total": { "type": "integer", "minimum": 0 } }, "required": [ "data", "total" ] }, "ClientContractLineListEnvelope": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/ClientContractLineListItem" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "ClientContractLineEnvelope": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/ClientContractLineResource" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "ContractLineIdParam": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." } }, "required": [ "id" ] }, "ContractLineServiceParam": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." }, "serviceId": { "type": "string", "format": "uuid", "description": "Service UUID from service_catalog.service_id." } }, "required": [ "id", "serviceId" ] }, "ContractLineTemplateParam": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Template UUID from plan_templates.template_id." } }, "required": [ "id" ] }, "ContractLineListQuery": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] }, "search": { "type": "string" }, "created_from": { "type": "string", "format": "date-time" }, "created_to": { "type": "string", "format": "date-time" }, "updated_from": { "type": "string", "format": "date-time" }, "updated_to": { "type": "string", "format": "date-time" }, "contract_line_name": { "type": "string" }, "contract_line_type": { "type": "string", "enum": [ "Fixed", "Hourly", "Usage" ] }, "billing_frequency": { "type": "string", "enum": [ "weekly", "bi-weekly", "monthly", "quarterly", "semi-annually", "annually" ] }, "is_custom": { "type": "string", "enum": [ "true", "false" ] }, "is_active": { "type": "string", "enum": [ "true", "false" ] }, "service_category": { "type": "string" }, "has_services": { "type": "string", "enum": [ "true", "false" ] }, "clients_count_min": { "type": "string" }, "clients_count_max": { "type": "string" }, "revenue_min": { "type": "string" }, "revenue_max": { "type": "string" }, "include_services": { "type": "string", "enum": [ "true", "false" ], "description": "Controller-level include flag parsed directly from query string." }, "include_usage": { "type": "string", "enum": [ "true", "false" ], "description": "Controller-level include flag parsed directly from query string." }, "include_clients": { "type": "string", "enum": [ "true", "false" ], "description": "Controller-level include flag parsed directly from query string." } } }, "ContractLineUsageMetricsQuery": { "type": "object", "properties": { "period_start": { "type": "string", "format": "date-time", "description": "Defaults to now minus 30 days when omitted." }, "period_end": { "type": "string", "format": "date-time", "description": "Defaults to now when omitted." } } }, "ContractLineBody": { "type": "object", "properties": { "contract_line_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "billing_frequency": { "type": "string", "enum": [ "weekly", "bi-weekly", "monthly", "quarterly", "semi-annually", "annually" ] }, "is_custom": { "type": "boolean" }, "service_category": { "type": "string" }, "contract_line_type": { "type": "string", "enum": [ "Fixed", "Hourly", "Usage" ] }, "cadence_owner": { "type": "string", "enum": [ "client", "contract" ] }, "hourly_rate": { "type": "number", "minimum": 0 }, "minimum_billable_time": { "type": "number", "minimum": 0 }, "round_up_to_nearest": { "type": "number", "minimum": 1 }, "enable_overtime": { "type": "boolean" }, "overtime_rate": { "type": "number", "minimum": 0 }, "overtime_threshold": { "type": "number", "minimum": 0 }, "enable_after_hours_rate": { "type": "boolean" }, "after_hours_multiplier": { "type": "number", "minimum": 0 }, "is_active": { "type": "boolean" }, "features": { "type": "array", "items": { "type": "string" } }, "location_id": { "type": [ "string", "null" ], "format": "uuid" } }, "required": [ "contract_line_name", "billing_frequency", "contract_line_type" ] }, "UpdateContractLineBody": { "type": "object", "properties": { "contract_line_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "billing_frequency": { "type": "string", "enum": [ "weekly", "bi-weekly", "monthly", "quarterly", "semi-annually", "annually" ] }, "is_custom": { "type": "boolean" }, "service_category": { "type": "string" }, "contract_line_type": { "type": "string", "enum": [ "Fixed", "Hourly", "Usage" ] }, "cadence_owner": { "type": "string", "enum": [ "client", "contract" ] }, "hourly_rate": { "type": "number", "minimum": 0 }, "minimum_billable_time": { "type": "number", "minimum": 0 }, "round_up_to_nearest": { "type": "number", "minimum": 1 }, "enable_overtime": { "type": "boolean" }, "overtime_rate": { "type": "number", "minimum": 0 }, "overtime_threshold": { "type": "number", "minimum": 0 }, "enable_after_hours_rate": { "type": "boolean" }, "after_hours_multiplier": { "type": "number", "minimum": 0 }, "is_active": { "type": "boolean" }, "features": { "type": "array", "items": { "type": "string" } }, "location_id": { "type": [ "string", "null" ], "format": "uuid" } } }, "ContractLineFixedConfigBody": { "type": "object", "properties": { "base_rate": { "type": "number", "minimum": 0 }, "enable_proration": { "type": "boolean" }, "billing_cycle_alignment": { "type": "string", "enum": [ "start", "end", "prorated" ] } } }, "ContractLineAddServiceBody": { "type": "object", "properties": { "service_id": { "type": "string", "format": "uuid" }, "quantity": { "type": "number", "minimum": 1 }, "custom_rate": { "type": "number", "minimum": 0 }, "configuration_type": { "type": "string", "enum": [ "Fixed", "Hourly", "Usage", "Bucket" ] }, "type_config": { "type": "object", "additionalProperties": {} } }, "required": [ "service_id" ] }, "ContractLineUpdateServiceBody": { "type": "object", "properties": { "quantity": { "type": "number", "minimum": 1 }, "custom_rate": { "type": "number", "minimum": 0 }, "type_config": { "type": "object", "additionalProperties": {} }, "rate_tiers": { "type": "array", "items": { "type": "object", "additionalProperties": {} } }, "user_type_rates": { "type": "array", "items": { "type": "object", "additionalProperties": {} } } } }, "ContractLineActivationBody": { "type": "object", "properties": { "is_active": { "type": "boolean" }, "effective_date": { "type": "string", "format": "date-time" }, "reason": { "type": "string" }, "notify_clients": { "type": "boolean" } }, "required": [ "is_active" ] }, "CopyContractLineBody": { "type": "object", "properties": { "source_contract_line_id": { "type": "string", "format": "uuid", "description": "Used by service; path id is currently ignored." }, "new_contract_line_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "copy_services": { "type": "boolean" }, "copy_configurations": { "type": "boolean" }, "modify_rates": { "type": "object", "properties": { "percentage_change": { "type": "number" }, "fixed_adjustment": { "type": "number" } } } }, "required": [ "source_contract_line_id", "new_contract_line_name" ] }, "BulkCreateContractLinesBody": { "type": "object", "properties": { "plans": { "type": "array", "items": { "$ref": "#/components/schemas/ContractLineBody" }, "minItems": 1, "maxItems": 50 } }, "required": [ "plans" ] }, "BulkUpdateContractLinesBody": { "type": "object", "properties": { "plans": { "type": "array", "items": { "type": "object", "properties": { "contract_line_id": { "type": "string", "format": "uuid" }, "data": { "$ref": "#/components/schemas/UpdateContractLineBody" } }, "required": [ "contract_line_id", "data" ] }, "minItems": 1, "maxItems": 50 } }, "required": [ "plans" ] }, "BulkDeleteContractLinesBody": { "type": "object", "properties": { "contract_line_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1, "maxItems": 50 } }, "required": [ "contract_line_ids" ] }, "BulkAddServicesBody": { "type": "object", "properties": { "contract_line_id": { "type": "string", "format": "uuid" }, "services": { "type": "array", "items": { "$ref": "#/components/schemas/ContractLineAddServiceBody" }, "minItems": 1, "maxItems": 20 } }, "required": [ "contract_line_id", "services" ] }, "BulkRemoveServicesBody": { "type": "object", "properties": { "contract_line_id": { "type": "string", "format": "uuid" }, "service_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1, "maxItems": 20 } }, "required": [ "contract_line_id", "service_ids" ] }, "CreateContractLineTemplateBody": { "type": "object", "properties": { "template_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "template_description": { "type": "string" }, "contract_line_type": { "type": "string", "enum": [ "Fixed", "Hourly", "Usage" ] }, "billing_frequency": { "type": "string", "enum": [ "weekly", "bi-weekly", "monthly", "quarterly", "semi-annually", "annually" ] }, "default_services": { "type": "array", "items": { "type": "object", "properties": { "service_id": { "type": "string", "format": "uuid" }, "configuration_type": { "type": "string", "enum": [ "Fixed", "Hourly", "Usage", "Bucket" ] }, "default_rate": { "type": "number", "minimum": 0 }, "quantity": { "type": "number", "minimum": 1 } }, "required": [ "service_id", "configuration_type" ] } }, "is_public": { "type": "boolean" } }, "required": [ "template_name", "contract_line_type", "billing_frequency" ] }, "CreateContractLineFromTemplateBody": { "type": "object", "properties": { "template_id": { "type": "string", "format": "uuid", "description": "Optional; the template id is taken from the path {id} and overrides this field when both are present." }, "contract_line_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "modify_rates": { "type": "object", "properties": { "percentage_change": { "type": "number" }, "fixed_adjustment": { "type": "number" } } }, "override_services": { "type": "array", "items": { "type": "object", "properties": { "service_id": { "type": "string", "format": "uuid" }, "custom_rate": { "type": "number", "minimum": 0 }, "quantity": { "type": "number", "minimum": 1 } }, "required": [ "service_id" ] } } }, "required": [ "contract_line_name" ] }, "ContractLineApiSuccess": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ] }, "data": { "anyOf": [ { "type": "object", "additionalProperties": {} }, { "type": "array", "items": { "type": "object", "additionalProperties": {} } } ] }, "meta": { "type": "object", "properties": { "timestamp": { "type": "string", "format": "date-time" }, "version": { "type": "string" } }, "required": [ "timestamp", "version" ] } }, "required": [ "success", "data" ] }, "ContractLineApiError": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ false ] }, "error": { "type": "object", "properties": { "message": { "type": "string" }, "code": { "type": "string" }, "details": {} }, "required": [ "message", "code" ] }, "meta": { "type": "object", "properties": { "timestamp": { "type": "string", "format": "date-time" }, "version": { "type": "string" } }, "required": [ "timestamp", "version" ] } }, "required": [ "success", "error" ] }, "DocumentDownloadParams": { "type": "object", "properties": { "fileId": { "type": "string", "format": "uuid", "description": "Document UUID or file UUID. The download handler resolves document_id first, then file_id." } }, "required": [ "fileId" ] }, "DocumentFileIdParams": { "type": "object", "properties": { "fileId": { "type": "string", "format": "uuid", "description": "File UUID from external_files.file_id identifying the stored file to serve." } }, "required": [ "fileId" ] }, "DocumentIdDownloadParams": { "type": "object", "properties": { "documentId": { "type": "string", "format": "uuid", "description": "Document UUID from documents.document_id, or file UUID from documents.file_id. The handler resolves document_id first and falls back to file_id." } }, "required": [ "documentId" ] }, "DocumentIdDownloadHeaders": { "type": "object", "properties": { "x-api-key": { "type": "string", "description": "Optional API key for machine-to-machine download requests. Used only when no valid Auth.js session cookie is present." } } }, "DocumentDownloadQuery": { "type": "object", "properties": { "format": { "type": "string", "enum": [ "pdf", "markdown", "md" ], "description": "Optional export format. pdf generates a PDF; markdown or md exports markdown; omitted downloads the original stored file." } } }, "DocumentViewHeaders": { "type": "object", "properties": { "range": { "type": "string", "description": "HTTP Range header for partial video responses, for example bytes=0-1048575. Only honored for video/* files." }, "x-api-key": { "type": "string", "description": "Optional API key fallback for non-browser clients. Used only when no valid session cookie is present." } } }, "BinaryFileResponse": { "type": "string", "description": "Binary file bytes streamed from the storage provider." }, "MarkdownFileResponse": { "type": "string", "description": "Markdown export text generated from document content." }, "DocumentDownloadError": { "type": "object", "properties": { "error": { "type": "string", "description": "Human-readable error message." }, "permissionError": { "type": "string", "description": "Permission-denied message returned by the underlying document action in some RBAC failures." } } }, "DocumentPlainTextError": { "type": "string", "description": "Plain text error response." }, "EmailErrorResponse": { "type": "object", "properties": { "error": { "type": "string", "description": "Human-readable error message." }, "success": { "type": "boolean", "description": "Present on some failure responses." }, "retryable": { "type": "boolean", "description": "Whether a webhook sender should retry the request." } }, "required": [ "error" ] }, "EmailOAuthInitiateRequest": { "type": "object", "properties": { "provider": { "type": "string", "enum": [ "microsoft", "google" ], "description": "Email OAuth provider to authorize." }, "redirectUri": { "type": "string", "format": "uri", "description": "Optional OAuth callback URI. If omitted, the server builds /api/auth/{provider}/callback from the configured base URL." }, "providerId": { "type": "string", "format": "uuid", "description": "Email provider configuration ID to carry through OAuth state and use during callback token persistence." } }, "required": [ "provider" ] }, "EmailOAuthInitiateResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "OAuth initiation succeeded." }, "authUrl": { "type": "string", "format": "uri", "description": "Provider authorization URL for the browser to visit." }, "provider": { "type": "string", "enum": [ "microsoft", "google" ], "description": "Email OAuth provider being authorized." }, "state": { "type": "string", "description": "Base64-encoded OAuth state containing tenant, userId, providerId, redirectUri, timestamp, nonce, and hosted flag." } }, "required": [ "success", "authUrl", "provider", "state" ] }, "EmailOAuthState": { "type": "object", "properties": { "tenant": { "type": "string", "description": "Tenant identifier from the authenticated user session." }, "userId": { "type": "string", "description": "User UUID from the authenticated session." }, "providerId": { "type": "string", "format": "uuid", "description": "Email provider configuration UUID from the initiate request." }, "redirectUri": { "type": "string", "description": "OAuth redirect URI included in the authorization request." }, "timestamp": { "type": "number", "description": "Epoch milliseconds when state was generated." }, "nonce": { "type": "string", "description": "Random hex nonce for CSRF correlation." }, "hosted": { "type": "boolean", "description": "Whether hosted credentials are used for the flow." } }, "required": [ "tenant", "redirectUri", "timestamp", "nonce" ] }, "EmailRefreshWatchRequest": { "type": "object", "properties": { "providerId": { "type": "string", "format": "uuid", "description": "Active Gmail email provider ID from email_providers.id to refresh." } }, "required": [ "providerId" ] }, "EmailRefreshWatchResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Refresh completed successfully." }, "message": { "type": "string", "description": "Human-readable success message." }, "providerId": { "type": "string", "format": "uuid", "description": "Email provider ID that was refreshed." }, "mailbox": { "type": "string", "format": "email", "description": "Mailbox address for the Gmail provider." } }, "required": [ "success", "message", "providerId", "mailbox" ] }, "GooglePubSubMessage": { "type": "object", "properties": { "data": { "type": "string", "description": "Base64-encoded Gmail notification JSON containing emailAddress and historyId." }, "messageId": { "type": "string", "description": "Google Pub/Sub message ID." }, "publishTime": { "type": "string", "description": "ISO timestamp when Pub/Sub published the message." } }, "required": [ "data", "messageId", "publishTime" ] }, "GooglePubSubPushBody": { "type": "object", "properties": { "message": { "$ref": "#/components/schemas/GooglePubSubMessage" }, "subscription": { "type": "string", "description": "Full Pub/Sub subscription path, such as projects/{project}/subscriptions/{subscriptionName}." } }, "required": [ "message", "subscription" ] }, "GmailNotificationPayload": { "type": "object", "properties": { "emailAddress": { "type": "string", "format": "email", "description": "Gmail mailbox address with new activity." }, "historyId": { "type": "string", "description": "Gmail history ID indicating the mailbox change position." } }, "required": [ "emailAddress", "historyId" ] }, "GoogleWebhookEnqueueResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Webhook was accepted." }, "queued": { "type": "boolean", "enum": [ true ], "description": "The notification was enqueued for asynchronous processing." }, "handoff": { "type": "string", "enum": [ "unified_pointer_queue" ], "description": "Queue handoff mechanism used for the Gmail notification pointer." }, "providerId": { "type": "string", "format": "uuid", "description": "Resolved email_providers.id for the Gmail mailbox." }, "tenant": { "type": "string", "description": "Tenant identifier owning the provider." }, "historyId": { "type": "string", "description": "Gmail history ID from the decoded notification." }, "jobId": { "type": "string", "format": "uuid", "description": "UUID assigned to the Redis queue job." }, "queueDepth": { "type": "integer", "description": "Redis queue depth after enqueue." } }, "required": [ "success", "queued", "handoff", "providerId", "tenant", "historyId", "jobId", "queueDepth" ] }, "GoogleWebhookSkippedResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Webhook request was accepted but no job was enqueued." }, "message": { "type": "string", "enum": [ "No data to process", "No provider found", "No google config found" ], "description": "Reason the webhook did not enqueue work." } }, "required": [ "success", "message" ] }, "MicrosoftWebhookValidationQuery": { "type": "object", "properties": { "validationtoken": { "type": "string", "description": "Microsoft Graph subscription validation token. The handler also accepts validationToken with camel-case spelling." }, "validationToken": { "type": "string", "description": "Camel-case variant of the Microsoft Graph validation token." } } }, "MicrosoftWebhookTextResponse": { "type": "string", "description": "Plain text response. For validation requests, this is the validation token echoed verbatim; otherwise it may be OK or Internal Server Error." }, "MicrosoftGraphResourceData": { "type": "object", "properties": { "@odata.type": { "type": "string", "description": "OData resource type, such as #microsoft.graph.message." }, "@odata.id": { "type": "string", "description": "OData resource identifier." }, "id": { "type": "string", "description": "Microsoft Graph message ID. If absent, the handler extracts it from resource." }, "subject": { "type": "string", "description": "Optional message subject supplied by Microsoft Graph." } } }, "MicrosoftGraphNotification": { "type": "object", "properties": { "changeType": { "type": "string", "description": "Microsoft Graph change type, typically created for new mail." }, "clientState": { "type": "string", "description": "Opaque verification token that must match microsoft_email_provider_config.webhook_verification_token when configured." }, "resource": { "type": "string", "description": "Graph resource path, for example /users/{userId}/messages/{messageId}." }, "resourceData": { "$ref": "#/components/schemas/MicrosoftGraphResourceData" }, "subscriptionExpirationDateTime": { "type": "string", "description": "Microsoft Graph subscription expiry timestamp." }, "subscriptionId": { "type": "string", "description": "Microsoft Graph subscription ID. Used to resolve microsoft_email_provider_config.webhook_subscription_id." }, "tenantId": { "type": "string", "description": "Microsoft tenant GUID from the notification. Informational only; tenant is resolved from DB." } }, "required": [ "changeType", "resource", "resourceData", "subscriptionId" ] }, "MicrosoftGraphWebhookBody": { "type": "object", "properties": { "value": { "type": "array", "items": { "$ref": "#/components/schemas/MicrosoftGraphNotification" }, "description": "Batch of Microsoft Graph change notifications." } }, "required": [ "value" ] }, "MicrosoftWebhookSuccessResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Webhook request was processed successfully." }, "queued": { "type": "boolean", "description": "Whether any notification pointer jobs were enqueued." }, "handoff": { "type": "string", "enum": [ "unified_pointer_queue" ], "description": "Queue handoff mechanism used for Microsoft notification pointers." }, "unifiedQueuedCount": { "type": "integer", "description": "Number of notification jobs enqueued." }, "processedCount": { "type": "integer", "description": "Number of notifications processed." }, "messageIds": { "type": "array", "items": { "type": "string" }, "description": "Microsoft Graph message IDs that were enqueued." } }, "required": [ "success", "queued", "handoff", "unifiedQueuedCount", "processedCount", "messageIds" ] }, "MicrosoftWebhookEmptyResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Webhook request was accepted." }, "message": { "type": "string", "description": "Reason no notification jobs were enqueued." } }, "required": [ "success", "message" ] }, "MicrosoftWebhookEnqueueErrorResponse": { "type": "object", "properties": { "error": { "type": "string", "enum": [ "Failed to enqueue one or more Microsoft pointer jobs" ] }, "failureCount": { "type": "integer", "description": "Number of enqueue failures." }, "failures": { "type": "array", "items": { "type": "object", "properties": { "subscriptionId": { "type": "string", "description": "Microsoft Graph subscription ID from the notification." }, "messageId": { "type": "string", "description": "Microsoft Graph message ID that failed to enqueue." }, "providerId": { "type": "string", "description": "Resolved email provider ID." }, "tenantId": { "type": "string", "description": "Resolved tenant identifier." }, "reason": { "type": "string", "description": "Failure reason." } }, "required": [ "subscriptionId", "messageId", "providerId", "tenantId", "reason" ] }, "description": "Per-notification enqueue failures." } }, "required": [ "error", "failureCount", "failures" ] }, "EmailWebhookTestRequest": { "type": "object", "properties": { "provider": { "type": "string", "enum": [ "microsoft", "google" ], "description": "Email provider type to simulate in the synthetic inbound email event. Defaults to microsoft." }, "messageId": { "type": "string", "description": "Synthetic message identifier to include in the test event. Defaults to test-message-123." } } }, "EmailWebhookTestResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Test event was published successfully." }, "message": { "type": "string", "description": "Human-readable success message." }, "eventId": { "type": "string", "description": "Redis Streams event ID returned by publishEvent." }, "tenant": { "type": "string", "description": "Tenant identifier from the authenticated user session." } }, "required": [ "success", "message", "eventId", "tenant" ] }, "ExtensionGatewayParams": { "type": "object", "properties": { "extensionId": { "type": "string", "minLength": 1, "description": "Extension identifier from the URL. This may be a registry/install UUID or a publisher.name slug, resolved case-insensitively." }, "path": { "type": "string", "description": "Remaining extension endpoint path after extensionId. The gateway forwards this path to the runner unchanged." } }, "required": [ "extensionId" ] }, "ExtensionGatewayHeaders": { "type": "object", "properties": { "x-request-id": { "type": "string", "format": "uuid", "description": "Optional request ID. The gateway generates a UUID when absent and forwards it to the runner." }, "x-idempotency-key": { "type": "string", "description": "Optional idempotency key for non-GET methods. The gateway falls back to x-request-id when absent and forwards the key to the runner." }, "x-alga-tenant": { "type": "string", "description": "Internal tenant header used for tenant resolution before session fallback." }, "x-tenant-id": { "type": "string", "description": "Legacy tenant header accepted for tenant resolution before session fallback." } } }, "ExtensionOpaqueRequest": { "type": "object", "properties": {}, "description": "Extension-specific request body. The gateway treats this as opaque, enforces a 10 MB limit for non-GET methods, base64-encodes it, and forwards it to the extension runner." }, "ExtensionOpaqueResponse": { "type": "object", "properties": {}, "description": "Extension-specific response relayed from the runner. Status code, content type, and body are controlled by the extension." }, "ExtensionGatewayErrorResponse": { "type": "object", "properties": { "error": { "type": "string", "enum": [ "not_installed", "payload_too_large", "install_context_missing", "runner_empty_response", "runner_invalid_response", "bad_gateway", "internal_error" ], "description": "Gateway-level error code." }, "detail": { "description": "Additional error detail when available." } }, "required": [ "error" ] }, "FinancialInvoiceUuidIdParam": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." } }, "required": [ "id" ] }, "FinancialInvoiceApiError": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] }, "FinancialInvoiceApiSuccess": { "type": "object", "properties": { "data": { "anyOf": [ { "type": "object", "additionalProperties": {} }, { "type": "array", "items": { "type": "object", "additionalProperties": {} } } ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "FinancialInvoiceApiPaginated": { "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object", "additionalProperties": {} } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" }, "hasNext": { "type": "boolean" }, "hasPrev": { "type": "boolean" } }, "required": [ "page", "limit", "total", "totalPages", "hasNext", "hasPrev" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data", "pagination" ] }, "FinancialListQuery": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] }, "search": { "type": "string" }, "created_from": { "type": "string" }, "created_to": { "type": "string" }, "updated_from": { "type": "string" }, "updated_to": { "type": "string" }, "client_id": { "type": "string", "format": "uuid" }, "invoice_id": { "type": "string", "format": "uuid" }, "type": { "type": "string" }, "status": { "type": "string" }, "amount_min": { "type": "string" }, "amount_max": { "type": "string" }, "include_expired": { "type": "string", "enum": [ "true", "false" ] }, "expiring_soon": { "type": "string", "enum": [ "true", "false" ] }, "has_remaining": { "type": "string", "enum": [ "true", "false" ] }, "has_expiration": { "type": "string", "enum": [ "true", "false" ] }, "date_from": { "type": "string" }, "date_to": { "type": "string" }, "group_by": { "type": "string", "enum": [ "day", "week", "month" ] }, "include_projections": { "type": "string", "enum": [ "true", "false" ] }, "as_of_date": { "type": "string" } } }, "InvoiceListQuery": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] }, "include_items": { "type": "string", "enum": [ "true", "false" ] }, "include_client": { "type": "string", "enum": [ "true", "false" ] }, "include_billing_cycle": { "type": "string", "enum": [ "true", "false" ] }, "include_transactions": { "type": "string", "enum": [ "true", "false" ] }, "q": { "type": "string" }, "from": { "type": "string" }, "to": { "type": "string" }, "format": { "type": "string", "enum": [ "json", "csv" ] } }, "additionalProperties": { "type": "string" } }, "InvoiceExecutionIdQuery": { "type": "object", "properties": { "execution_id": { "type": "string" }, "reason": { "type": "string" } } }, "BillingOverviewResponse": { "type": "object", "properties": { "data": { "type": "object", "additionalProperties": {} }, "count": { "type": "number" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "FinancialCalculateBillingBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "period_start": { "type": "string" }, "period_end": { "type": "string" } }, "required": [ "client_id", "period_start", "period_end" ] }, "FinancialApplyCreditToInvoiceBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "invoice_id": { "type": "string", "format": "uuid" }, "requested_amount": { "type": "number", "minimum": 0 } }, "required": [ "client_id", "invoice_id", "requested_amount" ] }, "FinancialPrepaymentInvoiceBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "amount": { "type": "number", "minimum": 0 }, "manual_expiration_date": { "type": "string" } }, "required": [ "client_id", "amount" ] }, "FinancialTransferCreditBody": { "type": "object", "properties": { "source_credit_id": { "type": "string", "format": "uuid" }, "target_client_id": { "type": "string", "format": "uuid" }, "amount": { "type": "number", "minimum": 0 }, "user_id": { "type": "string", "format": "uuid" }, "reason": { "type": "string" } }, "required": [ "source_credit_id", "target_client_id", "amount", "user_id" ] }, "FinancialValidateCreditBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" } }, "required": [ "client_id" ] }, "FinancialCreateTransactionBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "invoice_id": { "type": "string", "format": "uuid" }, "amount": { "type": "number" }, "type": { "type": "string" }, "status": { "type": "string" }, "parent_transaction_id": { "type": "string", "format": "uuid" }, "description": { "type": "string" }, "reference_number": { "type": "string" }, "metadata": { "type": "object", "additionalProperties": {} }, "balance_after": { "type": "number" }, "expiration_date": { "type": "string" }, "related_transaction_id": { "type": "string", "format": "uuid" } }, "required": [ "client_id", "amount", "type", "balance_after" ] }, "FinancialUpdateTransactionBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "invoice_id": { "type": "string", "format": "uuid" }, "amount": { "type": "number" }, "type": { "type": "string" }, "status": { "type": "string" }, "parent_transaction_id": { "type": "string", "format": "uuid" }, "description": { "type": "string" }, "reference_number": { "type": "string" }, "metadata": { "type": "object", "additionalProperties": {} }, "balance_after": { "type": "number" }, "expiration_date": { "type": "string" }, "related_transaction_id": { "type": "string", "format": "uuid" } } }, "FinancialCreatePaymentMethodBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "type": { "type": "string", "enum": [ "credit_card", "bank_account" ] }, "last4": { "type": "string", "minLength": 4, "maxLength": 4 }, "exp_month": { "type": "string" }, "exp_year": { "type": "string" }, "is_default": { "type": "boolean" }, "is_deleted": { "type": "boolean" } }, "required": [ "client_id", "type", "last4" ] }, "FinancialCalculateTaxBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "amount": { "type": "number", "minimum": 0 }, "tax_region": { "type": "string" }, "date": { "type": "string" } }, "required": [ "client_id", "amount", "tax_region" ] }, "FinancialBulkInvoiceOperationBody": { "type": "object", "properties": { "operation": { "type": "string", "enum": [ "send", "finalize", "cancel", "mark_paid" ] }, "invoice_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1 }, "options": { "type": "object", "additionalProperties": {} } }, "required": [ "operation", "invoice_ids" ] }, "FinancialBulkTransactionOperationBody": { "type": "object", "properties": { "operation": { "type": "string", "enum": [ "approve", "reject", "void" ] }, "transaction_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1 }, "options": { "type": "object", "additionalProperties": {} } }, "required": [ "operation", "transaction_ids" ] }, "FinancialBulkCreditOperationBody": { "type": "object", "properties": { "operation": { "type": "string", "enum": [ "expire", "extend", "transfer" ] }, "credit_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1 }, "options": { "type": "object", "additionalProperties": {} } }, "required": [ "operation", "credit_ids" ] }, "FinancialReconciliationResolveBody": { "type": "object", "properties": { "notes": { "type": "string" } } }, "InvoiceCreateBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "invoice_date": { "type": "string" }, "due_date": { "type": "string" }, "subtotal": { "type": "integer", "minimum": 0 }, "tax": { "type": "integer", "minimum": 0 }, "total_amount": { "type": "integer", "minimum": 0 }, "status": { "type": "string", "enum": [ "draft", "sent", "paid", "overdue", "cancelled", "pending", "prepayment", "partially_applied" ] }, "credit_applied": { "type": "integer", "minimum": 0 }, "is_manual": { "type": "boolean" }, "is_prepayment": { "type": "boolean" }, "items": { "type": "array", "items": { "type": "object", "additionalProperties": {} } } }, "required": [ "client_id", "invoice_date", "due_date", "subtotal", "tax", "total_amount", "status" ], "additionalProperties": {} }, "InvoiceUpdateBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "invoice_date": { "type": "string" }, "due_date": { "type": "string" }, "subtotal": { "type": "integer", "minimum": 0 }, "tax": { "type": "integer", "minimum": 0 }, "total_amount": { "type": "integer", "minimum": 0 }, "status": { "type": "string", "enum": [ "draft", "sent", "paid", "overdue", "cancelled", "pending", "prepayment", "partially_applied" ] }, "credit_applied": { "type": "integer", "minimum": 0 }, "is_manual": { "type": "boolean" }, "is_prepayment": { "type": "boolean" }, "items": { "type": "array", "items": { "type": "object", "additionalProperties": {} } } }, "additionalProperties": {} }, "InvoiceManualBody": { "type": "object", "properties": { "clientId": { "type": "string", "format": "uuid" }, "items": { "type": "array", "items": { "type": "object", "additionalProperties": {} }, "minItems": 1 }, "expirationDate": { "type": "string" }, "isPrepayment": { "type": "boolean" } }, "required": [ "clientId", "items" ] }, "InvoiceSelectorBody": { "type": "object", "properties": { "selector_input": { "type": "object", "properties": { "clientId": { "type": "string", "format": "uuid" }, "windowStart": { "type": "string" }, "windowEnd": { "type": "string" }, "executionWindow": { "type": "object", "properties": { "kind": { "type": "string", "enum": [ "client_cadence_window", "contract_cadence_window" ] }, "identityKey": { "type": "string" }, "cadenceOwner": { "type": "string", "enum": [ "client", "contract" ] }, "clientId": { "type": "string", "format": "uuid" }, "billingCycleId": { "type": [ "string", "null" ], "format": "uuid" }, "contractId": { "type": [ "string", "null" ], "format": "uuid" }, "contractLineId": { "type": [ "string", "null" ], "format": "uuid" }, "windowStart": { "type": [ "string", "null" ] }, "windowEnd": { "type": [ "string", "null" ] } }, "required": [ "kind", "identityKey", "cadenceOwner" ] } }, "required": [ "clientId", "windowStart", "windowEnd", "executionWindow" ] } }, "required": [ "selector_input" ] }, "InvoiceFinalizeBody": { "type": "object", "properties": { "finalized_at": { "type": "string" } } }, "InvoiceSendBody": { "type": "object", "properties": { "email_addresses": { "type": "array", "items": { "type": "string", "format": "email" }, "minItems": 1 }, "subject": { "type": "string" }, "message": { "type": "string" }, "include_pdf": { "type": "boolean" } }, "required": [ "email_addresses" ] }, "InvoiceCreditBody": { "type": "object", "properties": { "credit_amount": { "type": "integer", "minimum": 0 }, "transaction_id": { "type": "string", "format": "uuid" } }, "required": [ "credit_amount" ] }, "InvoicePaymentBody": { "type": "object", "properties": { "payment_amount": { "type": "integer", "minimum": 0 }, "payment_method": { "type": "string" }, "payment_date": { "type": "string" }, "reference_number": { "type": "string" }, "notes": { "type": "string" } }, "required": [ "payment_amount", "payment_method" ] }, "InvoiceTaxBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "amount": { "type": "integer", "minimum": 0 }, "tax_region": { "type": "string" }, "calculation_date": { "type": "string" } }, "required": [ "client_id", "amount", "tax_region" ] }, "InvoiceBulkStatusBody": { "type": "object", "properties": { "invoice_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1, "maxItems": 100 }, "status": { "type": "string", "enum": [ "draft", "sent", "paid", "overdue", "cancelled", "pending", "prepayment", "partially_applied" ] }, "finalized_at": { "type": "string" } }, "required": [ "invoice_ids", "status" ] }, "InvoiceBulkSendBody": { "type": "object", "properties": { "invoice_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1, "maxItems": 50 }, "email_template": { "type": "string" }, "include_pdf": { "type": "boolean" } }, "required": [ "invoice_ids" ] }, "InvoiceBulkDeleteBody": { "type": "object", "properties": { "ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1 }, "force": { "type": "boolean" } }, "required": [ "ids" ] }, "InvoiceBulkCreditBody": { "type": "object", "properties": { "invoice_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1, "maxItems": 100 }, "credit_amount_per_invoice": { "type": "integer", "minimum": 0 } }, "required": [ "invoice_ids", "credit_amount_per_invoice" ] }, "InvoiceRecurringCreateBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "frequency": { "type": "string", "enum": [ "daily", "weekly", "monthly", "quarterly", "annually" ] }, "start_date": { "type": "string" }, "end_date": { "type": "string" }, "is_active": { "type": "boolean" }, "invoice_template": { "type": "object", "additionalProperties": {} }, "max_generations": { "type": "number" } }, "required": [ "client_id", "name", "frequency", "start_date", "invoice_template" ] }, "KbArticle": { "type": "object", "properties": { "article_id": { "type": "string", "format": "uuid" }, "tenant": { "type": "string", "format": "uuid" }, "document_id": { "type": "string", "format": "uuid", "description": "Underlying document backing the article." }, "title": { "type": "string" }, "slug": { "type": "string" }, "article_type": { "type": "string", "enum": [ "how_to", "faq", "troubleshooting", "reference" ] }, "audience": { "type": "string", "enum": [ "internal", "client", "public" ] }, "status": { "type": "string", "enum": [ "draft", "review", "published", "archived" ] }, "category_id": { "type": [ "string", "null" ], "format": "uuid" }, "review_cycle_days": { "type": [ "integer", "null" ] }, "next_review_date": { "type": [ "string", "null" ] }, "published_at": { "type": [ "string", "null" ] }, "published_by": { "type": [ "string", "null" ], "format": "uuid" }, "document_name": { "type": [ "string", "null" ], "description": "Joined from the documents table." }, "created_at": { "type": "string" }, "updated_at": { "type": "string" } }, "required": [ "article_id", "tenant", "document_id", "title", "slug", "article_type", "audience", "status" ], "description": "A knowledge base article and its metadata." }, "KbArticleIdParam": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "KB article UUID." } }, "required": [ "id" ] }, "KbArticleFromTicketParam": { "type": "object", "properties": { "ticketId": { "type": "string", "format": "uuid", "description": "Source ticket UUID." } }, "required": [ "ticketId" ] }, "KbArticleCreateRequest": { "type": "object", "properties": { "title": { "type": "string", "minLength": 1, "maxLength": 255 }, "slug": { "type": "string", "description": "Optional; generated from the title when omitted." }, "article_type": { "type": "string", "enum": [ "how_to", "faq", "troubleshooting", "reference" ], "description": "Defaults to how_to." }, "audience": { "type": "string", "enum": [ "internal", "client", "public" ], "description": "Defaults to internal." }, "category_id": { "type": "string", "format": "uuid" }, "review_cycle_days": { "type": "integer" }, "content": { "type": "string", "description": "Initial body content." }, "content_format": { "type": "string", "enum": [ "markdown", "blocknote" ], "description": "Defaults to markdown." } }, "required": [ "title" ] }, "KbArticleUpdateRequest": { "type": "object", "properties": { "title": { "type": "string", "minLength": 1, "maxLength": 255 }, "slug": { "type": "string" }, "article_type": { "type": "string", "enum": [ "how_to", "faq", "troubleshooting", "reference" ] }, "audience": { "type": "string", "enum": [ "internal", "client", "public" ] }, "category_id": { "type": "string", "format": "uuid" }, "review_cycle_days": { "type": "integer" }, "status": { "type": "string", "enum": [ "draft", "review", "published", "archived" ] } } }, "KbArticleContentUpdateRequest": { "type": "object", "properties": { "content": { "type": "string", "minLength": 1 }, "format": { "type": "string", "enum": [ "markdown", "blocknote" ], "description": "Defaults to markdown." } }, "required": [ "content" ] }, "KbArticleListQuery": { "type": "object", "properties": { "page": { "type": "integer", "minimum": 1 }, "limit": { "type": "integer", "minimum": 1, "maximum": 100 }, "status": { "type": "string", "enum": [ "draft", "review", "published", "archived" ] }, "audience": { "type": "string", "enum": [ "internal", "client", "public" ] }, "article_type": { "type": "string", "enum": [ "how_to", "faq", "troubleshooting", "reference" ] }, "category_id": { "type": "string", "format": "uuid" }, "search": { "type": "string" } } }, "KbArticleResponse": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/KbArticle" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "PaginatedKbArticleResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/KbArticle" } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" }, "hasNext": { "type": "boolean" }, "hasPrev": { "type": "boolean" } }, "required": [ "page", "limit", "total", "totalPages", "hasNext", "hasPrev" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data", "pagination" ] }, "KbCategoryListResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object", "properties": { "category_id": { "type": "string", "format": "uuid" }, "category_name": { "type": "string" }, "display_order": { "type": "integer" } }, "required": [ "category_id", "category_name" ] } } }, "required": [ "data" ] }, "KbTemplateListResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object", "properties": { "template_id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "article_type": { "type": "string", "enum": [ "how_to", "faq", "troubleshooting", "reference" ] } }, "required": [ "template_id", "name" ] } } }, "required": [ "data" ] }, "KbArticleContentResponse": { "type": "object", "properties": { "data": { "type": "object", "properties": { "article_id": { "type": "string", "format": "uuid" }, "content": { "type": "string", "description": "Article body rendered as readable text." } }, "required": [ "content" ] } }, "required": [ "data" ] }, "PublicV1Success": { "type": "object", "properties": { "data": { "anyOf": [ { "type": "object", "additionalProperties": {} }, { "type": "array", "items": { "type": "object", "additionalProperties": {} } } ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "AiDocumentAssistBody": { "type": "object", "properties": { "instruction": { "type": "string", "description": "What the assistant should do." }, "documentContext": { "type": "string", "description": "Current document text/context." }, "documentId": { "type": "string", "format": "uuid" }, "tenantId": { "type": "string", "format": "uuid" }, "connectedUserNames": { "type": "array", "items": { "type": "string" } } }, "required": [ "instruction", "documentContext", "tenantId" ] }, "SoftwareSearchQuery": { "type": "object", "properties": { "search": { "type": "string" }, "category": { "type": "string" }, "software_type": { "type": "string" }, "is_managed": { "type": "string", "enum": [ "true", "false" ] }, "is_security_relevant": { "type": "string", "enum": [ "true", "false" ] }, "client_id": { "type": "string", "format": "uuid" }, "page": { "type": "integer", "minimum": 1 }, "limit": { "type": "integer", "minimum": 1, "maximum": 200 } } }, "PriorityListQuery": { "type": "object", "properties": { "page": { "type": "integer", "minimum": 1 }, "limit": { "type": "integer", "minimum": 1, "maximum": 100 }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] } } }, "PriorityIdParam": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" } }, "required": [ "id" ] }, "ExtensionActionBody": { "type": "object", "additionalProperties": {}, "description": "Extension install/uninstall payload (handled by the EE extension service)." }, "CompanyContractLineListQuery": { "type": "object", "properties": { "page": { "type": "integer", "minimum": 1 }, "limit": { "type": "integer", "minimum": 1, "maximum": 100 }, "client_id": { "type": "string", "format": "uuid" }, "contract_line_id": { "type": "string", "format": "uuid" } } }, "CompanyContractLineBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "contract_line_id": { "type": "string", "format": "uuid" }, "client_contract_id": { "type": "string", "format": "uuid" }, "custom_rate": { "type": "number" } }, "required": [ "client_id", "contract_line_id" ] }, "CompanyContractLineIdParam": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" } }, "required": [ "id" ] }, "XeroClientExportQuery": { "type": "object", "properties": { "clientIds": { "type": "string", "description": "Comma-separated client UUIDs to limit the export." } } }, "XeroClientImportQuery": { "type": "object", "properties": { "preview": { "type": "string", "enum": [ "true", "false" ] }, "createNew": { "type": "string", "enum": [ "true", "false" ] }, "updateExisting": { "type": "string", "enum": [ "true", "false" ] }, "matchBy": { "type": "string" } } }, "XeroTaxImportQuery": { "type": "object", "properties": { "preview": { "type": "string", "enum": [ "true", "false" ] } } }, "AccountingBatchIdParam": { "type": "object", "properties": { "batchId": { "type": "string", "description": "Export batch identifier." } }, "required": [ "batchId" ] }, "UnversionedV1Success": { "type": "object", "properties": { "data": { "anyOf": [ { "type": "object", "additionalProperties": {} }, { "type": "array", "items": { "type": "object", "additionalProperties": {} } } ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "ProjectTemplateIdParam": { "type": "object", "properties": { "templateId": { "type": "string", "format": "uuid" } }, "required": [ "templateId" ] }, "ProjectTemplateCopyOptions": { "type": "object", "properties": { "copyPhases": { "type": "boolean" }, "copyStatuses": { "type": "boolean" }, "copyTasks": { "type": "boolean" }, "copyDependencies": { "type": "boolean" }, "copyChecklists": { "type": "boolean" }, "copyServices": { "type": "boolean" }, "assignmentOption": { "type": "string" } }, "description": "Which parts of the template to copy when applying it." }, "ProjectTemplateCreateBody": { "type": "object", "properties": { "project_id": { "type": "string", "format": "uuid" }, "template_name": { "type": "string", "minLength": 1 }, "description": { "type": "string" }, "category": { "type": "string" } }, "required": [ "project_id", "template_name" ] }, "ProjectTemplateUpdateBody": { "type": "object", "properties": { "template_name": { "type": "string", "minLength": 1 }, "description": { "type": "string" }, "category": { "type": "string" } } }, "ProjectTemplateApplyBody": { "type": "object", "properties": { "project_name": { "type": "string", "minLength": 1 }, "client_id": { "type": "string", "format": "uuid" }, "start_date": { "type": "string", "format": "date-time" }, "assigned_to": { "type": "string", "format": "uuid" }, "options": { "$ref": "#/components/schemas/ProjectTemplateCopyOptions" } }, "required": [ "project_name", "client_id" ] }, "WorkflowDefinitionIdParam": { "type": "object", "properties": { "workflowId": { "type": "string", "format": "uuid" } }, "required": [ "workflowId" ] }, "WorkflowDefinitionVersionParams": { "type": "object", "properties": { "workflowId": { "type": "string", "format": "uuid" }, "version": { "type": "string", "description": "Draft version number (coerced to a positive integer)." } }, "required": [ "workflowId", "version" ] }, "WorkflowDefinitionDocument": { "type": "object", "additionalProperties": {}, "description": "Workflow definition DSL document (nodes, edges, triggers, payload schema, etc.)." }, "WorkflowDefinitionCreateBody": { "type": "object", "properties": { "key": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$", "description": "Stable workflow key; generated when omitted." }, "definition": { "$ref": "#/components/schemas/WorkflowDefinitionDocument" }, "payloadSchemaMode": { "type": "string", "enum": [ "inferred", "pinned" ] }, "pinnedPayloadSchemaRef": { "type": "string" } }, "required": [ "definition" ] }, "WorkflowImportQuery": { "type": "object", "properties": { "force": { "type": "string", "enum": [ "true", "false" ] } } }, "WorkflowImportBody": { "type": "object", "properties": { "bundle": { "description": "Legacy v1 workflow bundle document." } } }, "WorkflowMetadataBody": { "type": "object", "properties": { "key": { "type": "string" }, "isVisible": { "type": "boolean" }, "isPaused": { "type": "boolean" }, "concurrencyLimit": { "type": "integer", "minimum": 0 }, "autoPauseOnFailure": { "type": "boolean" }, "failureRateThreshold": { "type": "number", "minimum": 0, "maximum": 1 }, "failureRateMinRuns": { "type": "integer", "minimum": 0 }, "retentionPolicyOverride": { "type": "object", "additionalProperties": {} } } }, "WorkflowDefinitionUpdateBody": { "type": "object", "properties": { "definition": { "$ref": "#/components/schemas/WorkflowDefinitionDocument" } }, "required": [ "definition" ] }, "WorkflowPublishBody": { "type": "object", "properties": { "definition": { "$ref": "#/components/schemas/WorkflowDefinitionDocument" } } }, "WorkflowRunIdParam": { "type": "object", "properties": { "runId": { "type": "string" } }, "required": [ "runId" ] }, "WorkflowRunActionBody": { "type": "object", "properties": { "reason": { "type": "string", "description": "Audit reason for the action." }, "source": { "type": "string", "description": "Origin label; defaults to 'api'." } } }, "WorkflowRunCreateBody": { "type": "object", "properties": { "workflowId": { "type": "string" }, "workflowVersion": { "type": "integer", "exclusiveMinimum": 0 }, "payload": { "type": "object", "additionalProperties": {} }, "eventType": { "type": "string" }, "sourcePayloadSchemaRef": { "type": "string" } }, "required": [ "workflowId" ] }, "WorkflowRunReplayBody": { "type": "object", "properties": { "reason": { "type": "string" }, "source": { "type": "string" }, "payload": { "type": "object", "additionalProperties": {} } } }, "WorkflowEventBody": { "type": "object", "properties": { "eventName": { "type": "string" }, "workflowCorrelationKey": { "type": "string" }, "correlationKey": { "type": "string" }, "payloadSchemaRef": { "type": "string" }, "payload": { "type": "object", "additionalProperties": {} } }, "required": [ "eventName" ] }, "WorkflowEventIdParam": { "type": "object", "properties": { "eventId": { "type": "string" } }, "required": [ "eventId" ] }, "WorkflowJsonSchemaDocument": { "type": "object", "additionalProperties": {}, "description": "A JSON Schema document." }, "WorkflowRegistryAction": { "type": "object", "properties": { "id": { "type": "string", "description": "Stable action identifier (e.g. \"ticket.create\")." }, "version": { "type": "integer", "description": "Action version." }, "sideEffectful": { "type": "boolean", "description": "Whether executing the action mutates external state." }, "retryHint": { "type": [ "object", "null" ], "additionalProperties": {}, "description": "Suggested retry policy, or null." }, "idempotency": { "type": "object", "additionalProperties": {}, "description": "Idempotency configuration for the action." }, "ui": { "type": "object", "additionalProperties": {}, "description": "Designer UI metadata (label, group, icon, etc.)." }, "inputSchema": { "allOf": [ { "$ref": "#/components/schemas/WorkflowJsonSchemaDocument" }, { "description": "JSON Schema for the action input, annotated with designer presentation metadata." } ] }, "outputSchema": { "allOf": [ { "$ref": "#/components/schemas/WorkflowJsonSchemaDocument" }, { "description": "JSON Schema for the action output." } ] }, "examples": { "type": [ "array", "null" ], "items": { "type": "object", "additionalProperties": {} }, "description": "Example invocations, or null." } }, "required": [ "id", "version", "sideEffectful", "retryHint", "idempotency", "ui", "inputSchema", "outputSchema", "examples" ] }, "WorkflowRegistryNode": { "type": "object", "properties": { "id": { "type": "string", "description": "Node type identifier." }, "ui": { "type": "object", "additionalProperties": {}, "description": "Designer UI metadata for the node." }, "configSchema": { "allOf": [ { "$ref": "#/components/schemas/WorkflowJsonSchemaDocument" }, { "description": "JSON Schema for the node configuration." } ] }, "examples": { "type": [ "array", "null" ], "items": { "type": "object", "additionalProperties": {} }, "description": "Example configurations, or null." }, "defaultRetry": { "type": [ "object", "null" ], "additionalProperties": {}, "description": "Default retry policy for the node, or null." } }, "required": [ "id", "ui", "configSchema", "examples", "defaultRetry" ] }, "WorkflowSchemaRefParam": { "type": "object", "properties": { "schemaRef": { "type": "string", "description": "Registered schema reference; URL-encode it in the path." } }, "required": [ "schemaRef" ] }, "WorkflowSchemaResponse": { "type": "object", "properties": { "ref": { "type": "string", "description": "The resolved schema reference." }, "schema": { "$ref": "#/components/schemas/WorkflowJsonSchemaDocument" } }, "required": [ "ref", "schema" ] }, "FileDownloadParams": { "type": "object", "properties": { "fileId": { "type": "string", "format": "uuid", "description": "File UUID from external_files.file_id to download. The handler does not validate UUID syntax before querying storage metadata." } }, "required": [ "fileId" ] }, "FileBinaryDownloadResponse": { "type": "string", "description": "Binary file bytes loaded from the configured storage provider." }, "FileDownloadPlainTextError": { "type": "string", "description": "Plain text error response, such as Tenant not found or Download failed." }, "InboundWebhookConfig": { "type": "object", "properties": { "inboundWebhookId": { "type": "string", "format": "uuid" }, "tenant": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "slug": { "type": "string" }, "description": { "type": [ "string", "null" ] }, "authType": { "type": "string", "enum": [ "hmac_sha256", "bearer", "ip_allowlist", "path_token" ] }, "authConfig": { "type": "object", "additionalProperties": {}, "description": "Redacted auth configuration metadata." }, "idempotencySource": { "type": [ "object", "null" ], "properties": { "type": { "type": "string", "enum": [ "header", "jsonata" ] }, "value": { "type": "string" } }, "required": [ "type", "value" ] }, "idempotencyWindowSeconds": { "type": "integer" }, "handlerType": { "type": "string", "enum": [ "direct_action", "workflow" ] }, "handlerConfig": { "type": "object", "additionalProperties": {} }, "samplePayload": {}, "sampleCaptureExpiresAt": { "type": [ "string", "null" ], "format": "date-time" }, "isActive": { "type": "boolean" }, "rateLimitPerMinute": { "type": "integer" }, "autoDisabledAt": { "type": [ "string", "null" ], "format": "date-time" }, "createdBy": { "type": [ "string", "null" ], "format": "uuid" }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } }, "required": [ "inboundWebhookId", "tenant", "name", "slug", "description", "authType", "authConfig", "idempotencySource", "idempotencyWindowSeconds", "handlerType", "handlerConfig", "sampleCaptureExpiresAt", "isActive", "rateLimitPerMinute", "autoDisabledAt", "createdBy", "createdAt", "updatedAt" ] }, "InboundWebhookDelivery": { "type": "object", "properties": { "tenant": { "type": "string", "format": "uuid" }, "deliveryId": { "type": "string", "format": "uuid" }, "inboundWebhookId": { "type": [ "string", "null" ], "format": "uuid" }, "idempotencyKey": { "type": [ "string", "null" ] }, "receivedAt": { "type": "string", "format": "date-time" }, "requestMethod": { "type": "string" }, "requestPath": { "type": "string" }, "requestHeaders": { "type": "object", "additionalProperties": { "anyOf": [ { "type": "string" }, { "type": "array", "items": { "type": "string" } } ] } }, "requestBody": {}, "sourceIp": { "type": [ "string", "null" ] }, "userAgent": { "type": [ "string", "null" ] }, "authStatus": { "type": "string", "enum": [ "verified", "rejected_signature", "rejected_bearer", "rejected_ip", "rejected_no_auth" ] }, "dispatchStatus": { "type": "string", "enum": [ "pending", "dispatched", "duplicate", "failed" ] }, "handlerOutcome": { "type": [ "object", "null" ], "additionalProperties": {} }, "responseStatus": { "type": [ "integer", "null" ] }, "responseBody": {}, "durationMs": { "type": [ "integer", "null" ] }, "retryCount": { "type": "integer" }, "isReplay": { "type": "boolean" }, "replayedFrom": { "type": [ "string", "null" ], "format": "uuid" }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } }, "required": [ "tenant", "deliveryId", "inboundWebhookId", "idempotencyKey", "receivedAt", "requestMethod", "requestPath", "requestHeaders", "sourceIp", "userAgent", "authStatus", "dispatchStatus", "handlerOutcome", "responseStatus", "durationMs", "retryCount", "isReplay", "replayedFrom", "createdAt", "updatedAt" ] }, "InboundActionTargetField": { "type": "object", "properties": { "name": { "type": "string" }, "type": { "type": "string", "enum": [ "string", "int", "number", "boolean", "enum", "object", "array", "ref" ] }, "required": { "type": "boolean" }, "description": { "type": "string" }, "enumValues": { "type": "array", "items": { "type": "string" } }, "refEntityType": { "type": "string" } }, "required": [ "name", "type", "required", "description" ] }, "InboundActionDefinition": { "type": "object", "properties": { "name": { "type": "string" }, "entityType": { "type": "string" }, "displayName": { "type": "string" }, "description": { "type": "string" }, "targetFields": { "type": "array", "items": { "$ref": "#/components/schemas/InboundActionTargetField" } } }, "required": [ "name", "entityType", "displayName", "description", "targetFields" ] }, "InboundWebhookCreateInput": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1 }, "slug": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$" }, "description": { "type": [ "string", "null" ] }, "auth_type": { "type": "string", "enum": [ "hmac_sha256", "bearer", "ip_allowlist", "path_token" ] }, "auth_config": { "type": "object", "additionalProperties": {} }, "idempotency_source": { "type": [ "object", "null" ], "properties": { "type": { "type": "string", "enum": [ "header", "jsonata" ] }, "value": { "type": "string" } }, "required": [ "type", "value" ] }, "idempotency_window_seconds": { "type": "integer", "exclusiveMinimum": 0 }, "handler_type": { "type": "string", "enum": [ "direct_action", "workflow" ] }, "handler_config": { "type": "object", "additionalProperties": {} }, "is_active": { "type": "boolean" }, "rate_limit_per_minute": { "type": "integer", "exclusiveMinimum": 0 } }, "required": [ "name", "slug", "auth_type", "auth_config", "handler_type", "handler_config" ] }, "InboundWebhookAuthConfig": { "oneOf": [ { "type": "object", "properties": { "auth_type": { "type": "string", "enum": [ "hmac_sha256" ] }, "auth_config": { "type": "object", "properties": { "signature_header": { "type": "string", "description": "Header containing sha256= or raw hex HMAC." }, "secret": { "type": "string", "description": "Create/update-time secret; never returned after storage." }, "secret_vault_path": { "type": "string", "description": "Stored vault path metadata returned by reads." } }, "required": [ "signature_header" ] } }, "required": [ "auth_type", "auth_config" ] }, { "type": "object", "properties": { "auth_type": { "type": "string", "enum": [ "bearer" ] }, "auth_config": { "type": "object", "properties": { "token": { "type": "string", "description": "Create/update-time bearer token; never returned after storage." }, "token_vault_path": { "type": "string", "description": "Stored vault path metadata returned by reads." } } } }, "required": [ "auth_type", "auth_config" ] }, { "type": "object", "properties": { "auth_type": { "type": "string", "enum": [ "ip_allowlist" ] }, "auth_config": { "type": "object", "properties": { "ip_cidrs": { "type": "array", "items": { "type": "string" }, "description": "Exact IP strings or CIDR ranges accepted." } }, "required": [ "ip_cidrs" ] } }, "required": [ "auth_type", "auth_config" ] }, { "type": "object", "properties": { "auth_type": { "type": "string", "enum": [ "path_token" ] }, "auth_config": { "type": "object", "properties": { "query_param": { "type": "string", "description": "Query parameter name, default token." }, "token": { "type": "string", "description": "Create/update-time path token; never returned after storage." }, "token_vault_path": { "type": "string", "description": "Stored vault path metadata returned by reads." } } } }, "required": [ "auth_type", "auth_config" ] } ] }, "InboundWebhookHandlerConfig": { "oneOf": [ { "type": "object", "properties": { "handler_type": { "type": "string", "enum": [ "direct_action" ] }, "handler_config": { "type": "object", "properties": { "action": { "type": "string", "description": "Registered inbound action name." }, "field_mapping": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Map of target field name to JSONata expression evaluated against the request body." } }, "required": [ "action", "field_mapping" ] } }, "required": [ "handler_type", "handler_config" ] }, { "type": "object", "properties": { "handler_type": { "type": "string", "enum": [ "workflow" ] }, "handler_config": { "type": "object", "properties": { "workflow_id": { "type": "string", "format": "uuid", "description": "Workflow definition to start for verified deliveries." } }, "required": [ "workflow_id" ] } }, "required": [ "handler_type", "handler_config" ] } ] }, "InboundWebhookUpdateInput": { "allOf": [ { "$ref": "#/components/schemas/InboundWebhookCreateInput" }, { "type": "object", "properties": { "inbound_webhook_id": { "type": "string", "format": "uuid" } } } ] }, "WorkflowWebhookEnvelope": { "type": "object", "properties": { "source": { "type": "string", "description": "Inbound webhook slug." }, "body": { "description": "Parsed JSON request body as received." }, "headers": { "type": "object", "additionalProperties": { "anyOf": [ { "type": "string" }, { "type": "array", "items": { "type": "string" } } ] }, "description": "Filtered safe request headers." }, "verified": { "type": "boolean", "enum": [ true ] }, "delivery_id": { "type": "string", "format": "uuid" }, "idempotency_key": { "type": [ "string", "null" ] }, "received_at": { "type": "string", "format": "date-time" } }, "required": [ "source", "headers", "verified", "delivery_id", "idempotency_key", "received_at" ] }, "HealthResponse": { "type": "object", "properties": { "status": { "type": "string", "enum": [ "ok" ], "description": "Always ok when the API process is reachable." }, "version": { "type": "string", "description": "Hardcoded API version string returned by the handler." } }, "required": [ "status", "version" ] }, "HealthzResponse": { "type": "object", "properties": { "status": { "type": "string", "enum": [ "healthy" ], "description": "Always healthy when the process can serve HTTP." }, "timestamp": { "type": "string", "format": "date-time", "description": "ISO timestamp generated when the liveness check is handled." }, "uptime": { "type": "number", "minimum": 0, "description": "Process uptime in seconds from process.uptime()." }, "version": { "type": "string", "description": "Application version from the environment/package metadata, with a fallback in the handler." }, "environment": { "type": "string", "description": "NODE_ENV value returned by the Next.js /api/healthz handler." } }, "required": [ "status", "timestamp", "uptime", "version" ] }, "ReadyzChecks": { "type": "object", "properties": { "database": { "type": "boolean", "description": "True when a SELECT 1 database connectivity check succeeds." }, "redis": { "type": "boolean", "description": "Redis readiness placeholder. Currently mirrors the database check result." } }, "required": [ "database", "redis" ] }, "ReadyzReadyResponse": { "type": "object", "properties": { "status": { "type": "string", "enum": [ "ready" ], "description": "All critical dependencies checked by this handler are available." }, "timestamp": { "type": "string", "format": "date-time", "description": "ISO timestamp generated when the readiness check is handled." }, "uptime": { "type": "number", "minimum": 0, "description": "Process uptime in seconds from process.uptime()." }, "version": { "type": "string", "description": "Application version from npm_package_version, with handler fallback." }, "checks": { "$ref": "#/components/schemas/ReadyzChecks" } }, "required": [ "status", "timestamp", "uptime", "version", "checks" ] }, "ReadyzNotReadyResponse": { "type": "object", "properties": { "status": { "type": "string", "enum": [ "not_ready" ], "description": "One or more critical dependencies are unavailable." }, "timestamp": { "type": "string", "format": "date-time", "description": "ISO timestamp generated when the readiness check is handled." }, "uptime": { "type": "number", "minimum": 0, "description": "Process uptime in seconds. Present in the dependency-failure branch, omitted by the catch branch." }, "version": { "type": "string", "description": "Application version. Present only in the ready response branch." }, "checks": { "$ref": "#/components/schemas/ReadyzChecks" }, "error": { "type": "string", "description": "Readiness failure reason or caught exception message." } }, "required": [ "status", "timestamp", "checks", "error" ] }, "RunnerCanaryHeaders": { "type": "object", "properties": { "x-canary": { "type": "string", "description": "Optional runner canary identifier used only for logging and cache variance." } } }, "InstallLookupByHostQuery": { "type": "object", "properties": { "host": { "type": "string", "minLength": 1, "description": "Runner domain hostname to resolve. The implementation lowercases the value and strips any port before matching tenant_extension_install.runner_domain." } }, "required": [ "host" ] }, "InstallLookupByHostResponse": { "type": "object", "properties": { "tenant_id": { "type": "string", "description": "Tenant ID from tenant_extension_install.tenant_id for the matching runner domain." }, "extension_id": { "type": "string", "format": "uuid", "description": "Extension registry UUID from tenant_extension_install.registry_id." }, "content_hash": { "type": "string", "description": "Content hash from extension_bundle.content_hash for the install version. Usually sha256:<64 hex chars>." } }, "required": [ "tenant_id", "extension_id", "content_hash" ] }, "InstallLookupErrorResponse": { "type": "object", "properties": { "error": { "type": "string", "description": "Error message, such as missing host, not found, internal error, or middleware API-key errors." } }, "required": [ "error" ] }, "InstallValidateQuery": { "type": "object", "properties": { "tenant": { "type": "string", "format": "uuid", "description": "Tenant UUID from tenant_extension_install.tenant_id." }, "extension": { "type": "string", "format": "uuid", "description": "Extension registry UUID from tenant_extension_install.registry_id." }, "hash": { "type": "string", "minLength": 1, "description": "Bundle content hash to validate. The EE action accepts sha256:<64 hex chars> or a raw 64-character hex string and normalizes raw hex to sha256: form." } }, "required": [ "tenant", "extension", "hash" ] }, "InstallValidateResponse": { "type": "object", "properties": { "valid": { "type": "boolean", "description": "True when the hash matches a bundle for the currently installed extension version; false when validation completes but does not match." } }, "required": [ "valid" ] }, "InstallValidateParameterErrorResponse": { "type": "object", "properties": { "valid": { "type": "boolean", "enum": [ false ], "description": "Always false for parameter errors." }, "error": { "type": "string", "enum": [ "missing or invalid parameters" ], "description": "Returned when tenant, extension, or hash is absent." } }, "required": [ "valid", "error" ] }, "QboOAuthCallbackQuery": { "type": "object", "properties": { "code": { "type": "string", "description": "OAuth 2.0 authorization code returned by Intuit. Required unless Intuit sends an error parameter." }, "state": { "type": "string", "description": "Base64url-encoded JSON containing tenantId and csrf generated by the connect endpoint." }, "realmId": { "type": "string", "description": "QuickBooks company/realm ID returned by Intuit." }, "error": { "type": "string", "description": "OAuth error returned by Intuit when authorization fails or is denied." } } }, "QboErrorResponse": { "type": "object", "properties": { "error": { "type": "string", "description": "Error message returned by middleware or by the connect endpoint." } }, "required": [ "error" ] }, "QboRouteMissingResponse": { "type": "string", "description": "Default Next.js 404 page or not-found response. The route file listed in inventory is absent in this worktree." }, "QuickBooksV1MappingIdParam": { "type": "object", "properties": { "mapping_id": { "type": "string", "description": "Mapping identifier extracted from path segment mapping_id." } }, "required": [ "mapping_id" ] }, "QuickBooksV1SyncIdParam": { "type": "object", "properties": { "sync_id": { "type": "string", "description": "Sync operation identifier extracted from path segment sync_id." } }, "required": [ "sync_id" ] }, "QuickBooksV1EntityQuery": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "active": { "type": "string", "enum": [ "true", "false" ] }, "search": { "type": "string" }, "entity_type": { "type": "string" }, "force_refresh": { "type": "string", "enum": [ "true", "false" ] } } }, "QuickBooksV1SyncHistoryQuery": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "status": { "type": "string", "enum": [ "pending", "in_progress", "completed", "failed", "cancelled", "partial" ] }, "operation_type": { "type": "string", "enum": [ "customer_sync", "invoice_export", "invoice_import", "payment_sync", "item_sync", "tax_sync", "full_sync", "test_connection" ] }, "date_from": { "type": "string" }, "date_to": { "type": "string" } } }, "QuickBooksV1OAuthInitiateBody": { "type": "object", "properties": { "state": { "type": "string", "minLength": 1 }, "redirect_uri": { "type": "string", "format": "uri" }, "scope": { "type": "string" } }, "required": [ "state" ] }, "QuickBooksV1OAuthCallbackBody": { "type": "object", "properties": { "code": { "type": "string", "minLength": 1 }, "state": { "type": "string", "minLength": 1 }, "realmId": { "type": "string", "minLength": 1 }, "error": { "type": "string" }, "error_description": { "type": "string" } }, "required": [ "code", "state", "realmId" ] }, "QuickBooksV1ConnectionTestBody": { "type": "object", "properties": { "testType": { "type": "string", "enum": [ "clientInfo", "items", "customers", "full" ] }, "forceRefresh": { "type": "boolean" } } }, "QuickBooksV1CustomerSyncBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "sync_type": { "type": "string", "enum": [ "create", "update", "bidirectional" ] }, "force_update": { "type": "boolean" }, "include_inactive": { "type": "boolean" } } }, "QuickBooksV1InvoiceExportBody": { "type": "object", "properties": { "invoice_id": { "type": "string", "format": "uuid" }, "date_range": { "type": "object", "properties": { "start_date": { "type": "string" }, "end_date": { "type": "string" } }, "required": [ "start_date", "end_date" ] }, "status_filter": { "type": "array", "items": { "type": "string", "enum": [ "draft", "sent", "paid", "overdue", "cancelled" ] } }, "client_id": { "type": "string", "format": "uuid" }, "export_format": { "type": "string", "enum": [ "qbo", "json" ] }, "include_line_items": { "type": "boolean" }, "auto_create_items": { "type": "boolean" }, "skip_existing": { "type": "boolean" } } }, "QuickBooksV1InvoiceImportBody": { "type": "object", "properties": { "qbo_invoice_id": { "type": "string" }, "date_range": { "type": "object", "properties": { "start_date": { "type": "string" }, "end_date": { "type": "string" } }, "required": [ "start_date", "end_date" ] }, "import_payments": { "type": "boolean" }, "auto_create_clients": { "type": "boolean" }, "update_existing": { "type": "boolean" } } }, "QuickBooksV1PaymentSyncBody": { "type": "object", "properties": { "payment_id": { "type": "string", "format": "uuid" }, "invoice_id": { "type": "string", "format": "uuid" }, "date_range": { "type": "object", "properties": { "start_date": { "type": "string" }, "end_date": { "type": "string" } }, "required": [ "start_date", "end_date" ] }, "sync_type": { "type": "string", "enum": [ "create", "update", "bidirectional" ] }, "include_unapplied": { "type": "boolean" } } }, "QuickBooksV1AccountMappingsBody": { "type": "object", "properties": { "mappings": { "type": "array", "items": { "type": "object", "properties": { "account_type": { "type": "string", "enum": [ "income", "expense", "asset", "liability", "equity" ] }, "alga_account_name": { "type": "string" }, "qbo_account_id": { "type": "string" }, "is_default": { "type": "boolean" } }, "required": [ "account_type", "alga_account_name", "qbo_account_id" ] }, "minItems": 1 }, "replace_existing": { "type": "boolean" } }, "required": [ "mappings" ] }, "QuickBooksV1TaxMappingsBody": { "type": "object", "properties": { "mappings": { "type": "array", "items": { "type": "object", "properties": { "alga_tax_region": { "type": "string" }, "qbo_tax_code_id": { "type": "string" }, "is_default": { "type": "boolean" } }, "required": [ "alga_tax_region", "qbo_tax_code_id" ] }, "minItems": 1 }, "replace_existing": { "type": "boolean" } }, "required": [ "mappings" ] }, "QuickBooksV1DataMappingBody": { "type": "object", "properties": { "entity_type": { "type": "string", "enum": [ "customer", "invoice", "payment", "item", "tax_code" ] }, "mapping_name": { "type": "string" }, "field_mappings": { "type": "array", "items": { "type": "object", "properties": { "alga_field": { "type": "string" }, "qbo_field": { "type": "string" }, "transform_function": { "type": "string" }, "is_required": { "type": "boolean" }, "default_value": {}, "validation_rule": { "type": "string" } }, "required": [ "alga_field", "qbo_field" ] }, "minItems": 1 }, "is_default": { "type": "boolean" }, "description": { "type": "string" } }, "required": [ "entity_type", "mapping_name", "field_mappings" ] }, "QuickBooksV1BulkSyncBody": { "type": "object", "properties": { "operations": { "type": "array", "items": { "type": "object", "properties": { "operation_type": { "type": "string", "enum": [ "customer_sync", "invoice_export", "invoice_import", "payment_sync", "item_sync", "tax_sync", "full_sync", "test_connection" ] }, "entity_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "qbo_entity_ids": { "type": "array", "items": { "type": "string" } }, "date_range": { "type": "object", "properties": { "start_date": { "type": "string" }, "end_date": { "type": "string" } }, "required": [ "start_date", "end_date" ] }, "parameters": { "type": "object", "additionalProperties": {} } }, "required": [ "operation_type" ] }, "minItems": 1, "maxItems": 10 }, "execution_mode": { "type": "string", "enum": [ "sequential", "parallel" ] }, "stop_on_error": { "type": "boolean" }, "notification_email": { "type": "string", "format": "email" } }, "required": [ "operations" ] }, "QuickBooksV1HealthConfigBody": { "type": "object", "properties": { "monitoring_enabled": { "type": "boolean" }, "check_interval": { "type": "integer", "minimum": 1 }, "alert_thresholds": { "type": "object", "additionalProperties": {} } } }, "QuickBooksV1ApiError": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] }, "QuickBooksV1ApiSuccess": { "type": "object", "properties": { "data": { "anyOf": [ { "type": "object", "additionalProperties": {} }, { "type": "array", "items": { "type": "object", "additionalProperties": {} } } ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "QuickBooksV1ApiPaginated": { "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object", "additionalProperties": {} } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" }, "hasNext": { "type": "boolean" }, "hasPrev": { "type": "boolean" } }, "required": [ "page", "limit", "total", "totalPages", "hasNext", "hasPrev" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data", "pagination" ] }, "BoardIdParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Board UUID." } }, "required": [ "id" ] }, "BoardApiResponse": { "type": "object", "properties": { "board_id": { "type": "string", "format": "uuid" }, "board_name": { "type": "string" }, "description": { "type": [ "string", "null" ] }, "display_order": { "type": "number" }, "is_default": { "type": "boolean" }, "is_inactive": { "type": "boolean" }, "category_type": { "type": [ "string", "null" ], "enum": [ "custom", "itil" ] }, "priority_type": { "type": [ "string", "null" ], "enum": [ "custom", "itil" ] }, "display_itil_impact": { "type": [ "boolean", "null" ] }, "display_itil_urgency": { "type": [ "boolean", "null" ] }, "default_assigned_to": { "type": [ "string", "null" ], "format": "uuid" }, "inbound_reply_reopen_enabled": { "type": "boolean" }, "inbound_reply_reopen_cutoff_hours": { "type": "integer" }, "inbound_reply_reopen_status_id": { "type": [ "string", "null" ], "format": "uuid" }, "inbound_reply_ai_ack_suppression_enabled": { "type": "boolean" }, "enable_live_ticket_timer": { "type": [ "boolean", "null" ] }, "tenant": { "type": "string", "format": "uuid" } }, "required": [ "board_id", "board_name", "description", "display_order", "is_default", "is_inactive", "category_type", "priority_type", "display_itil_impact", "display_itil_urgency", "default_assigned_to", "enable_live_ticket_timer", "tenant" ] }, "BoardEnvelope": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/BoardApiResponse" } }, "required": [ "data" ] }, "BoardListEnvelope": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/BoardApiResponse" } } }, "required": [ "data" ] }, "BoardCreateRequest": { "type": "object", "properties": { "board_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "description": { "type": "string", "maxLength": 1000 }, "is_default": { "type": "boolean" }, "is_inactive": { "type": "boolean" }, "category_type": { "type": "string", "enum": [ "custom", "itil" ] }, "priority_type": { "type": "string", "enum": [ "custom", "itil" ] }, "default_assigned_to": { "type": [ "string", "null" ], "format": "uuid" }, "display_itil_impact": { "type": "boolean" }, "display_itil_urgency": { "type": "boolean" }, "enable_live_ticket_timer": { "type": "boolean" } }, "required": [ "board_name" ], "description": "Payload for creating a new board." }, "BoardUpdateRequest": { "type": "object", "properties": { "board_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "description": { "type": "string", "maxLength": 1000 }, "is_default": { "type": "boolean" }, "is_inactive": { "type": "boolean" }, "category_type": { "type": "string", "enum": [ "custom", "itil" ] }, "priority_type": { "type": "string", "enum": [ "custom", "itil" ] }, "default_assigned_to": { "type": [ "string", "null" ], "format": "uuid" }, "display_itil_impact": { "type": "boolean" }, "display_itil_urgency": { "type": "boolean" }, "enable_live_ticket_timer": { "type": "boolean" } }, "description": "Payload for updating a board. All fields are optional." }, "StatusIdParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Status UUID." } }, "required": [ "id" ] }, "StatusApiResponse": { "type": "object", "properties": { "status_id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "status_type": { "type": "string", "enum": [ "ticket", "project", "project_task", "interaction" ] }, "order_number": { "type": "number" }, "is_closed": { "type": "boolean" }, "is_default": { "type": [ "boolean", "null" ] }, "item_type": { "type": [ "string", "null" ] }, "standard_status_id": { "type": [ "string", "null" ] }, "is_custom": { "type": [ "boolean", "null" ] }, "tenant": { "type": "string", "format": "uuid" } }, "required": [ "status_id", "name", "status_type", "order_number", "is_closed", "is_default", "item_type", "standard_status_id", "is_custom", "tenant" ] }, "StatusEnvelope": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/StatusApiResponse" } }, "required": [ "data" ] }, "StatusListEnvelope": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/StatusApiResponse" } } }, "required": [ "data" ] }, "StatusCreateRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 255 }, "status_type": { "type": "string", "enum": [ "ticket", "project", "project_task", "interaction" ] }, "board_id": { "type": "string", "format": "uuid" }, "is_closed": { "type": "boolean" }, "is_default": { "type": "boolean" }, "order_number": { "type": "integer", "minimum": 0 }, "color": { "type": "string", "pattern": "^#[0-9A-Fa-f]{6}$" }, "icon": { "type": "string", "maxLength": 50 } }, "required": [ "name", "status_type" ], "description": "Payload for creating a new status. For ticket statuses, board_id is required." }, "StatusUpdateRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 255 }, "is_closed": { "type": "boolean" }, "is_default": { "type": "boolean" }, "order_number": { "type": "integer", "minimum": 0 }, "color": { "type": "string", "pattern": "^#[0-9A-Fa-f]{6}$" }, "icon": { "type": "string", "maxLength": 50 } }, "description": "Payload for updating a status. All fields are optional." }, "SearchQuery": { "type": "object", "properties": { "query": { "type": "string", "minLength": 1, "maxLength": 200, "description": "Full-text query. A concise expression of likely record words, identifiers, names, or quoted phrases — not a full sentence. Supports OR for alternatives (e.g. \"laptop OR workstation\")." }, "types": { "type": "string", "description": "Comma-separated list of object types to restrict the search (e.g. \"ticket,project\"). Omit to search every type the API key's user is permitted to read. Allowed values: client, contact, user, ticket, ticket_comment, project, project_phase, project_task, project_task_comment, asset, invoice, invoice_item, invoice_annotation, contract, client_contract, document, kb_article, service_catalog, service_request_submission, service_request_definition, workflow_task, interaction, schedule_entry, time_entry, board, category, tag, status." }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, "description": "Maximum number of results to return. Defaults to 30; capped at 100." }, "cursor": { "type": "string", "description": "Opaque pagination cursor copied from a prior response's nextCursor." }, "sort": { "type": "string", "enum": [ "relevance", "recent" ], "description": "Result ordering. \"relevance\" (default) ranks by full-text score; \"recent\" orders by last update." } }, "required": [ "query" ] }, "SearchResultRow": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "client", "contact", "user", "ticket", "ticket_comment", "project", "project_phase", "project_task", "project_task_comment", "asset", "invoice", "invoice_item", "invoice_annotation", "contract", "client_contract", "document", "kb_article", "service_catalog", "service_request_submission", "service_request_definition", "workflow_task", "interaction", "schedule_entry", "time_entry", "board", "category", "tag", "status" ], "description": "Indexed business object type." }, "id": { "type": "string", "description": "Object identifier within its type." }, "parentId": { "type": "string", "description": "Identifier of the parent record for nested results (e.g. the ticket of a ticket comment)." }, "title": { "type": "string", "description": "Primary display label for the record." }, "subtitle": { "type": "string", "description": "Secondary context line." }, "snippet": { "type": "string", "description": "Matched-text excerpt with tags around the highlighted terms." }, "url": { "type": "string", "description": "Relative in-app URL pointing at the record." }, "score": { "type": "number", "description": "Relevance score; higher is more relevant." }, "updatedAt": { "type": "string", "format": "date-time", "description": "Source record last-updated timestamp." } }, "required": [ "type", "id", "title", "url", "score", "updatedAt" ] }, "SearchResultData": { "type": "object", "properties": { "results": { "type": "array", "items": { "$ref": "#/components/schemas/SearchResultRow" }, "description": "Matching records for this page, ordered by the requested sort." }, "groups": { "type": "object", "additionalProperties": { "type": "integer" }, "description": "Total match counts keyed by object type (across all pages), before the page limit is applied." }, "totalCount": { "type": "integer", "description": "Total number of matches across all permitted types." }, "nextCursor": { "type": "string", "description": "Cursor for the next page; absent when the result set fits within the page." } }, "required": [ "results", "groups", "totalCount" ] }, "SearchResponse": { "type": "object", "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/SearchResultData" }, { "description": "Search payload returned by createSuccessResponse." } ] } }, "required": [ "data" ] }, "SearchApiErrorEnvelope": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string", "description": "Machine-readable error code such as VALIDATION_ERROR, UNAUTHORIZED, or INTERNAL_ERROR." }, "message": { "type": "string", "description": "Human-readable error message." }, "details": { "description": "Optional structured details, including Zod validation errors." } }, "required": [ "code", "message" ] } }, "required": [ "error" ] }, "ServiceCategory": { "type": "object", "properties": { "tenant": { "type": "string", "format": "uuid" }, "category_id": { "type": "string", "format": "uuid" }, "category_name": { "type": "string" }, "description": { "type": [ "string", "null" ] }, "display_order": { "type": "integer", "default": 0 }, "is_active": { "type": "boolean" }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" }, "created_by": { "type": "string", "format": "uuid" }, "updated_by": { "type": "string", "format": "uuid" }, "tags": { "type": "array", "items": { "type": "string" } } }, "required": [ "tenant", "category_id", "category_name", "is_active", "created_at", "updated_at", "created_by", "updated_by" ], "description": "Service category resource." }, "ServiceCategoryCreateRequest": { "type": "object", "properties": { "category_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "description": { "type": "string", "maxLength": 1000 }, "is_active": { "type": "boolean" } }, "required": [ "category_name" ], "description": "Payload for creating a service category." }, "ServiceCategoryListQuery": { "type": "object", "properties": { "search": { "type": "string" }, "is_active": { "type": "boolean" }, "page": { "type": "integer", "minimum": 1 }, "limit": { "type": "integer", "minimum": 1, "maximum": 100 } }, "description": "Query parameters for listing service categories." }, "CategoryIdParam": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Category UUID from service_categories.category_id or categories.category_id." } }, "required": [ "id" ] }, "CategoryBoardIdParam": { "type": "object", "properties": { "boardId": { "type": "string", "format": "uuid", "description": "Board UUID used by ticket category tree queries." } }, "required": [ "boardId" ] }, "TicketCategory": { "type": "object", "properties": { "category_id": { "type": "string", "format": "uuid" }, "category_name": { "type": "string" }, "parent_category": { "type": [ "string", "null" ], "format": "uuid" }, "board_id": { "type": [ "string", "null" ], "format": "uuid" }, "display_order": { "type": "number" }, "is_from_itil_standard": { "type": [ "boolean", "null" ] }, "created_by": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "tenant": { "type": "string", "format": "uuid" }, "children": { "type": "array", "items": {} }, "depth": { "type": "number" }, "path": { "type": "string" }, "children_count": { "type": "number" } }, "required": [ "category_id", "category_name", "board_id", "created_by", "tenant" ], "description": "Ticket category resource from the categories table, including optional hierarchy fields. The categories table has no description/updated_by/updated_at columns." }, "TicketCategoryCreateRequest": { "type": "object", "properties": { "category_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "board_id": { "type": "string", "format": "uuid" }, "parent_category": { "type": "string", "format": "uuid" } }, "required": [ "category_name", "board_id" ] }, "TicketCategoryUpdateRequest": { "type": "object", "properties": { "category_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "board_id": { "type": "string", "format": "uuid" }, "parent_category": { "type": "string", "format": "uuid" } } }, "TicketCategoryListQuery": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] }, "search": { "type": "string" }, "created_from": { "type": "string", "format": "date-time" }, "created_to": { "type": "string", "format": "date-time" }, "updated_from": { "type": "string", "format": "date-time" }, "updated_to": { "type": "string", "format": "date-time" }, "category_name": { "type": "string" }, "board_id": { "type": "string", "format": "uuid" }, "parent_category": { "type": "string", "format": "uuid" }, "is_parent": { "type": "string", "enum": [ "true", "false" ] }, "is_child": { "type": "string", "enum": [ "true", "false" ] }, "depth": { "type": "string" }, "active": { "type": "string", "enum": [ "true", "false" ] }, "offset": { "type": "string" }, "sort_by": { "type": "string" }, "sort_order": { "type": "string", "enum": [ "asc", "desc" ] }, "include_hierarchy": { "type": "string", "enum": [ "true", "false" ] }, "category_type": { "type": "string", "enum": [ "service", "ticket" ] } } }, "CategoryMoveRequest": { "type": "object", "properties": { "category_id": { "type": "string", "format": "uuid" }, "new_parent_id": { "type": [ "string", "null" ], "format": "uuid" }, "position": { "type": "integer", "minimum": 0 } }, "required": [ "category_id" ] }, "CategorySearchQuery": { "type": "object", "properties": { "search_term": { "type": "string", "minLength": 1 }, "category_type": { "type": "string", "enum": [ "service", "ticket" ] }, "board_id": { "type": "string", "format": "uuid" }, "include_inactive": { "type": "string", "enum": [ "true", "false" ] }, "limit": { "type": "string" }, "offset": { "type": "string" } }, "required": [ "search_term" ] }, "CategoryAnalyticsQuery": { "type": "object", "properties": { "category_type": { "type": "string", "enum": [ "service", "ticket" ] }, "board_id": { "type": "string", "format": "uuid" }, "date_from": { "type": "string", "format": "date-time" }, "date_to": { "type": "string", "format": "date-time" }, "include_usage": { "type": "string", "enum": [ "true", "false" ] } } }, "CategoryAnalyticsResponse": { "type": "object", "properties": { "data": { "type": "object", "properties": { "analytics": { "type": "object", "additionalProperties": {}, "description": "Aggregated category analytics from CategoryService.getCategoryAnalytics." }, "generated_at": { "type": "string", "format": "date-time" } }, "required": [ "analytics", "generated_at" ] } }, "required": [ "data" ] }, "CategoryTreeResponse": { "type": "object", "properties": { "data": { "type": "object", "properties": { "tree": { "type": "array", "items": {}, "description": "Hierarchical category tree nodes from CategoryService.getCategoryTree." }, "total_categories": { "type": "integer", "minimum": 0 } }, "required": [ "tree", "total_categories" ] } }, "required": [ "data" ] }, "BulkDeleteCategoriesRequest": { "type": "object", "properties": { "category_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "minItems": 1, "maxItems": 50 }, "category_type": { "type": "string", "enum": [ "service", "ticket" ] }, "force": { "anyOf": [ { "type": "string", "enum": [ "true", "false" ] }, { "type": "boolean" } ], "description": "Optional force flag; schema accepts both boolean and string boolean values." } }, "required": [ "category_ids", "category_type" ] }, "BulkDeleteCategoriesResponse": { "type": "object", "properties": { "data": { "type": "object", "properties": { "message": { "type": "string" }, "success": { "type": "integer", "minimum": 0 }, "failed": { "type": "integer", "minimum": 0 }, "errors": { "type": "array", "items": { "type": "object", "additionalProperties": {} } } }, "required": [ "message", "success", "failed" ] } }, "required": [ "data" ] }, "PaginatedTicketCategoryResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/TicketCategory" } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" }, "hasNext": { "type": "boolean" }, "hasPrev": { "type": "boolean" } }, "required": [ "page", "limit", "total", "totalPages", "hasNext", "hasPrev" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data", "pagination" ] }, "PaginatedServiceCategoryResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/ServiceCategory" } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" }, "hasNext": { "type": "boolean" }, "hasPrev": { "type": "boolean" } }, "required": [ "page", "limit", "total", "totalPages", "hasNext", "hasPrev" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data", "pagination" ] }, "ServiceIdParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Service UUID." } }, "required": [ "id" ] }, "ServicePrice": { "type": "object", "properties": { "currency_code": { "type": "string", "minLength": 3, "maxLength": 3 }, "rate": { "type": "number" } }, "required": [ "currency_code", "rate" ] }, "ServiceCatalogEntry": { "type": "object", "properties": { "service_id": { "type": "string", "format": "uuid" }, "tenant": { "type": "string", "format": "uuid" }, "service_name": { "type": "string" }, "custom_service_type_id": { "type": "string", "format": "uuid" }, "billing_method": { "type": "string", "enum": [ "fixed", "hourly", "usage" ] }, "default_rate": { "type": "number" }, "unit_of_measure": { "type": "string" }, "category_id": { "type": [ "string", "null" ], "format": "uuid" }, "tax_rate_id": { "type": [ "string", "null" ], "format": "uuid" }, "description": { "type": [ "string", "null" ] }, "item_kind": { "type": "string", "enum": [ "service", "product" ] }, "is_active": { "type": "boolean" }, "sku": { "type": [ "string", "null" ] }, "cost": { "type": [ "number", "null" ] }, "cost_currency": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 3 }, "vendor": { "type": [ "string", "null" ] }, "manufacturer": { "type": [ "string", "null" ] }, "product_category": { "type": [ "string", "null" ] }, "is_license": { "type": "boolean" }, "license_term": { "type": [ "string", "null" ] }, "license_billing_cadence": { "type": [ "string", "null" ] }, "service_type_name": { "type": "string" }, "prices": { "type": "array", "items": { "$ref": "#/components/schemas/ServicePrice" } } }, "required": [ "service_id", "tenant", "service_name", "custom_service_type_id", "billing_method", "default_rate", "unit_of_measure" ], "description": "Service catalog entry." }, "ServiceEnvelope": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/ServiceCatalogEntry" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "PaginatedServiceEnvelope": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/ServiceCatalogEntry" } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" }, "hasNext": { "type": "boolean" }, "hasPrev": { "type": "boolean" } }, "required": [ "page", "limit", "total", "totalPages", "hasNext", "hasPrev" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data", "pagination" ] }, "ProductIdParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Product UUID." } }, "required": [ "id" ] }, "ProductPrice": { "type": "object", "properties": { "currency_code": { "type": "string", "minLength": 3, "maxLength": 3 }, "rate": { "type": "number" } }, "required": [ "currency_code", "rate" ] }, "ProductCatalogEntry": { "type": "object", "properties": { "service_id": { "type": "string", "format": "uuid" }, "tenant": { "type": "string", "format": "uuid" }, "service_name": { "type": "string" }, "custom_service_type_id": { "type": "string", "format": "uuid" }, "billing_method": { "type": "string", "enum": [ "usage" ] }, "default_rate": { "type": "number" }, "unit_of_measure": { "type": "string" }, "category_id": { "type": [ "string", "null" ], "format": "uuid" }, "tax_rate_id": { "type": [ "string", "null" ], "format": "uuid" }, "description": { "type": [ "string", "null" ] }, "item_kind": { "type": "string", "enum": [ "product" ] }, "is_active": { "type": "boolean" }, "sku": { "type": [ "string", "null" ] }, "cost": { "type": [ "number", "null" ] }, "cost_currency": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 3 }, "vendor": { "type": [ "string", "null" ] }, "manufacturer": { "type": [ "string", "null" ] }, "product_category": { "type": [ "string", "null" ] }, "is_license": { "type": "boolean" }, "license_term": { "type": [ "string", "null" ] }, "license_billing_cadence": { "type": [ "string", "null" ] }, "service_type_name": { "type": "string" }, "prices": { "type": "array", "items": { "$ref": "#/components/schemas/ProductPrice" } } }, "required": [ "service_id", "tenant", "service_name", "custom_service_type_id", "billing_method", "default_rate", "unit_of_measure", "item_kind" ], "description": "Product catalog entry." }, "ProductEnvelope": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/ProductCatalogEntry" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "PaginatedProductEnvelope": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/ProductCatalogEntry" } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" }, "hasNext": { "type": "boolean" }, "hasPrev": { "type": "boolean" } }, "required": [ "page", "limit", "total", "totalPages", "hasNext", "hasPrev" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data", "pagination" ] }, "ServiceTypeIdParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Service type UUID." } }, "required": [ "id" ] }, "ServiceTypeListQuery": { "type": "object", "properties": { "search": { "type": "string" }, "is_active": { "type": "boolean" }, "page": { "type": "integer", "minimum": 1 }, "limit": { "type": "integer", "minimum": 1, "maximum": 100 } } }, "ServiceTypeResource": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "tenant": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "is_active": { "type": "boolean" }, "description": { "type": [ "string", "null" ] }, "order_number": { "type": "integer" }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" } }, "required": [ "id", "tenant", "name", "is_active", "order_number", "created_at", "updated_at" ], "description": "Tenant-specific service type." }, "ServiceTypeEnvelope": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/ServiceTypeResource" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "PaginatedServiceTypeEnvelope": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/ServiceTypeResource" } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" }, "hasNext": { "type": "boolean" }, "hasPrev": { "type": "boolean" } }, "required": [ "page", "limit", "total", "totalPages", "hasNext", "hasPrev" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data", "pagination" ] }, "WebhookIdParamV1": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." } }, "required": [ "id" ] }, "WebhookDeliveryParamsV1": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "delivery_id": { "type": "string", "format": "uuid", "description": "Delivery UUID from webhook_deliveries.delivery_id (URL-derived)." } }, "required": [ "id", "delivery_id" ] }, "WebhookListQueryV1": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] }, "name": { "type": "string" }, "url": { "type": "string" }, "event_type": { "type": "string" }, "is_active": { "type": "string", "enum": [ "true", "false" ] }, "is_test_mode": { "type": "string", "enum": [ "true", "false" ] }, "payload_format": { "type": "string", "enum": [ "json", "xml", "form_data", "custom" ] }, "has_failures": { "type": "string", "enum": [ "true", "false" ] }, "last_delivery_from": { "type": "string" }, "last_delivery_to": { "type": "string" }, "delivery_rate_min": { "type": "string" }, "delivery_rate_max": { "type": "string" }, "query": { "type": "string" } } }, "WebhookAnalyticsQueryV1": { "type": "object", "properties": { "webhook_id": { "type": "string", "format": "uuid" }, "date_from": { "type": "string", "format": "date-time" }, "date_to": { "type": "string", "format": "date-time" } } }, "WebhookDeliveryQueryV1": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "status": { "type": "string", "enum": [ "pending", "delivered", "failed", "retrying", "abandoned" ] }, "from_date": { "type": "string", "format": "date-time" }, "to_date": { "type": "string", "format": "date-time" } } }, "TicketWebhookEventV1": { "type": "string", "enum": [ "ticket.created", "ticket.updated", "ticket.status_changed", "ticket.assigned", "ticket.closed", "ticket.comment.added" ], "description": "Ticket-domain webhook events emitted by the eventBus subscriber." }, "WebhookEventTypeV1": { "type": "string", "enum": [ "ticket.created", "ticket.updated", "ticket.status_changed", "ticket.assigned", "ticket.closed", "ticket.comment.added", "project.created", "project.updated", "project.status_changed", "project.assigned", "project.closed", "project.completed", "project.task.created", "project.task.updated", "project.task.status_changed", "project.task.assigned", "project.task.completed", "client.created", "client.updated", "contact.created", "contact.updated", "time.entry.created", "time.entry.updated", "time.entry.approved", "invoice.created", "invoice.finalized", "invoice.sent", "invoice.paid", "asset.created", "asset.updated", "asset.maintenance.scheduled", "asset.maintenance.completed", "system.backup.completed", "system.backup.failed", "system.maintenance.started", "system.maintenance.completed", "workflow.execution.started", "workflow.execution.completed", "workflow.execution.failed", "custom.event" ] }, "CreateWebhookBodyV1": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1 }, "description": { "type": "string" }, "url": { "type": "string", "format": "uri" }, "method": { "type": "string", "enum": [ "GET", "POST", "PUT", "PATCH", "DELETE" ] }, "event_types": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookEventTypeV1" }, "minItems": 1 }, "security": { "type": "object", "additionalProperties": {} }, "payload_format": { "type": "string", "enum": [ "json", "xml", "form_data", "custom" ] }, "content_type": { "type": "string" }, "custom_headers": { "type": "object", "additionalProperties": { "type": "string" } }, "event_filter": { "type": "object", "additionalProperties": {} }, "payload_fields": { "type": [ "object", "null" ], "properties": { "ticket": { "type": [ "array", "null" ], "items": { "type": "string", "enum": [ "ticket_number", "title", "url", "status_id", "status_name", "is_closed", "previous_status_id", "previous_status_name", "priority_id", "priority_name", "client_id", "client_name", "contact_name_id", "contact_name", "contact_email", "assigned_to", "assigned_to_name", "assigned_team_id", "board_id", "board_name", "category_id", "subcategory_id", "entered_at", "updated_at", "closed_at", "due_date", "tags", "comment", "changes", "comments" ] } }, "project": { "type": [ "array", "null" ], "items": { "type": "string", "enum": [ "project_name", "wbs_code", "description", "status_id", "status_name", "is_closed", "previous_status_id", "previous_status_name", "client_id", "client_name", "contact_name_id", "contact_name", "contact_email", "assigned_to", "assigned_to_name", "start_date", "end_date", "budgeted_hours", "url", "changes", "phases", "task_counts", "task_id", "phase_id", "phase_name", "task_name", "estimated_hours", "actual_hours", "due_date", "priority_id", "priority_name", "tags" ] } } }, "description": "Per-entity payload allowlist. null/{} = full payload everywhere. { ticket: null } = full ticket payload. { ticket: [] } = required-only. { ticket: [a,b] } = explicit allowlist plus required keys. Unknown entities and fields are rejected by WEBHOOK_PAYLOAD_FIELDS_BY_ENTITY in lib/webhooks/payloadFields. Supported fields: ticket: ticket_number, title, url, status_id, status_name, is_closed, previous_status_id, previous_status_name, priority_id, priority_name, client_id, client_name, contact_name_id, contact_name, contact_email, assigned_to, assigned_to_name, assigned_team_id, board_id, board_name, category_id, subcategory_id, entered_at, updated_at, closed_at, due_date, tags, comment, changes, comments; project: project_name, wbs_code, description, status_id, status_name, is_closed, previous_status_id, previous_status_name, client_id, client_name, contact_name_id, contact_name, contact_email, assigned_to, assigned_to_name, start_date, end_date, budgeted_hours, url, changes, phases, task_counts, task_id, phase_id, phase_name, task_name, estimated_hours, actual_hours, due_date, priority_id, priority_name, tags." }, "payload_transformation": { "type": "object", "additionalProperties": {} }, "retry_config": { "type": "object", "additionalProperties": {} }, "is_active": { "type": "boolean" }, "is_test_mode": { "type": "boolean" }, "verify_ssl": { "type": "boolean" }, "secret_token": { "type": "string" }, "rate_limit": { "type": "object", "additionalProperties": {} }, "metadata": { "type": "object", "additionalProperties": {} }, "tags": { "type": "array", "items": { "type": "string" } } }, "required": [ "name", "url", "event_types" ] }, "WebhookTestBodyV1": { "type": "object", "properties": { "webhook_id": { "type": "string", "format": "uuid" }, "test_event_type": { "$ref": "#/components/schemas/WebhookEventTypeV1" }, "test_payload": { "type": "object", "additionalProperties": {} }, "override_url": { "type": "string", "format": "uri" } }, "required": [ "test_event_type" ] }, "WebhookTemplateBodyV1": { "type": "object", "properties": { "name": { "type": "string" }, "description": { "type": "string" }, "category": { "type": "string" }, "default_config": { "type": "object", "additionalProperties": {} }, "required_fields": { "type": "array", "items": { "type": "string" } }, "supported_events": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookEventTypeV1" } }, "is_system_template": { "type": "boolean" } }, "required": [ "name", "category", "default_config" ] }, "WebhookTemplateCreateBodyV1": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1 }, "url": { "type": "string", "format": "uri" }, "custom_config": { "type": "object", "additionalProperties": {} } }, "required": [ "name", "url" ] }, "WebhookSignatureBodyV1": { "type": "object", "properties": { "algorithm": { "type": "string", "enum": [ "sha1", "sha256", "sha512" ], "description": "Only sha256 is currently honored; other values yield valid=false." }, "signature": { "type": "string", "description": "Either the raw v1 signature, or the canonical \"t=,v1=\" header value. When raw and a timestamp is supplied, the controller assembles the canonical form before verifying." }, "timestamp": { "type": "number", "description": "Unix-seconds timestamp paired with a raw v1 signature." }, "body": { "type": "string", "description": "Exact request body bytes to verify against." }, "webhook_id": { "type": "string", "format": "uuid", "description": "Resolves the signing secret from webhooks.signing_secret_vault_path." }, "secret_vault_path": { "type": "string", "description": "Vault key (basename of secret_vault_path) used when no webhook_id is supplied. webhook_id or secret_vault_path is required." } }, "required": [ "algorithm", "signature", "body" ], "description": "Verification request: webhook_id OR secret_vault_path is required." }, "WebhookApiErrorV1": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] }, "WebhookRecordV1": { "type": "object", "properties": { "webhook_id": { "type": "string", "format": "uuid" }, "tenant": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "description": { "type": [ "string", "null" ] }, "url": { "type": "string" }, "method": { "type": "string", "enum": [ "GET", "POST", "PUT", "PATCH", "DELETE" ] }, "event_types": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookEventTypeV1" } }, "security": { "type": [ "object", "null" ], "additionalProperties": {} }, "payload_format": { "type": "string", "enum": [ "json", "xml", "form_data", "custom" ] }, "content_type": { "type": "string" }, "custom_headers": { "type": [ "object", "null" ], "additionalProperties": { "type": "string" } }, "event_filter": { "type": [ "object", "null" ], "additionalProperties": {} }, "payload_fields": { "type": [ "object", "null" ], "properties": { "ticket": { "type": [ "array", "null" ], "items": { "type": "string", "enum": [ "ticket_number", "title", "url", "status_id", "status_name", "is_closed", "previous_status_id", "previous_status_name", "priority_id", "priority_name", "client_id", "client_name", "contact_name_id", "contact_name", "contact_email", "assigned_to", "assigned_to_name", "assigned_team_id", "board_id", "board_name", "category_id", "subcategory_id", "entered_at", "updated_at", "closed_at", "due_date", "tags", "comment", "changes", "comments" ] } }, "project": { "type": [ "array", "null" ], "items": { "type": "string", "enum": [ "project_name", "wbs_code", "description", "status_id", "status_name", "is_closed", "previous_status_id", "previous_status_name", "client_id", "client_name", "contact_name_id", "contact_name", "contact_email", "assigned_to", "assigned_to_name", "start_date", "end_date", "budgeted_hours", "url", "changes", "phases", "task_counts", "task_id", "phase_id", "phase_name", "task_name", "estimated_hours", "actual_hours", "due_date", "priority_id", "priority_name", "tags" ] } } }, "description": "Per-entity payload allowlist. null/{} = full payload everywhere. { ticket: null } = full ticket payload. { ticket: [] } = required-only. { ticket: [a,b] } = explicit allowlist plus required keys. Unknown entities and fields are rejected by WEBHOOK_PAYLOAD_FIELDS_BY_ENTITY in lib/webhooks/payloadFields. Supported fields: ticket: ticket_number, title, url, status_id, status_name, is_closed, previous_status_id, previous_status_name, priority_id, priority_name, client_id, client_name, contact_name_id, contact_name, contact_email, assigned_to, assigned_to_name, assigned_team_id, board_id, board_name, category_id, subcategory_id, entered_at, updated_at, closed_at, due_date, tags, comment, changes, comments; project: project_name, wbs_code, description, status_id, status_name, is_closed, previous_status_id, previous_status_name, client_id, client_name, contact_name_id, contact_name, contact_email, assigned_to, assigned_to_name, start_date, end_date, budgeted_hours, url, changes, phases, task_counts, task_id, phase_id, phase_name, task_name, estimated_hours, actual_hours, due_date, priority_id, priority_name, tags." }, "payload_transformation": { "type": [ "object", "null" ], "additionalProperties": {} }, "retry_config": { "type": [ "object", "null" ], "additionalProperties": {} }, "is_active": { "type": "boolean" }, "is_test_mode": { "type": "boolean" }, "verify_ssl": { "type": "boolean" }, "total_deliveries": { "type": "integer" }, "successful_deliveries": { "type": "integer" }, "failed_deliveries": { "type": "integer" }, "last_delivery_at": { "type": [ "string", "null" ], "format": "date-time" }, "last_success_at": { "type": [ "string", "null" ], "format": "date-time" }, "last_failure_at": { "type": [ "string", "null" ], "format": "date-time" }, "auto_disabled_at": { "type": [ "string", "null" ], "format": "date-time" }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" }, "tags": { "type": "array", "items": { "type": "string" } } }, "required": [ "webhook_id", "tenant", "name", "description", "url", "method", "event_types", "custom_headers", "is_active", "verify_ssl", "total_deliveries", "successful_deliveries", "failed_deliveries", "created_at", "updated_at" ] }, "WebhookDeliveryRecordV1": { "type": "object", "properties": { "tenant": { "type": "string", "format": "uuid" }, "delivery_id": { "type": "string", "format": "uuid" }, "webhook_id": { "type": "string", "format": "uuid" }, "event_id": { "type": "string" }, "event_type": { "type": "string" }, "request_headers": { "type": [ "object", "null" ], "additionalProperties": { "type": "string" } }, "request_body": {}, "response_status_code": { "type": [ "integer", "null" ] }, "response_headers": { "type": [ "object", "null" ], "additionalProperties": { "type": "string" } }, "response_body": { "type": [ "string", "null" ] }, "status": { "type": "string", "enum": [ "pending", "delivered", "failed", "retrying", "abandoned" ] }, "attempt_number": { "type": "integer" }, "duration_ms": { "type": [ "integer", "null" ] }, "error_message": { "type": [ "string", "null" ] }, "next_retry_at": { "type": [ "string", "null" ], "format": "date-time" }, "is_test": { "type": "boolean" }, "attempted_at": { "type": "string", "format": "date-time" }, "completed_at": { "type": [ "string", "null" ], "format": "date-time" } }, "required": [ "tenant", "delivery_id", "webhook_id", "event_id", "event_type", "request_headers", "response_status_code", "response_headers", "response_body", "status", "attempt_number", "duration_ms", "error_message", "next_retry_at", "is_test", "attempted_at", "completed_at" ] }, "WebhookHealthV1": { "type": "object", "properties": { "webhook_id": { "type": "string", "format": "uuid" }, "status": { "type": "string", "enum": [ "healthy", "failing", "disabled" ] }, "is_active": { "type": "boolean" }, "auto_disabled_at": { "type": [ "string", "null" ], "format": "date-time" }, "total_deliveries": { "type": "integer" }, "successful_deliveries": { "type": "integer" }, "failed_deliveries": { "type": "integer" }, "success_rate": { "type": "number", "description": "Fraction in [0,1]; 1 when there are no deliveries yet." }, "last_delivery_at": { "type": [ "string", "null" ], "format": "date-time" }, "last_success_at": { "type": [ "string", "null" ], "format": "date-time" }, "last_failure_at": { "type": [ "string", "null" ], "format": "date-time" }, "checked_at": { "type": "string", "format": "date-time" } }, "required": [ "webhook_id", "status", "is_active", "auto_disabled_at", "total_deliveries", "successful_deliveries", "failed_deliveries", "success_rate", "last_delivery_at", "last_success_at", "last_failure_at", "checked_at" ] }, "WebhookTestResultV1": { "type": "object", "properties": { "test_id": { "type": "string", "format": "uuid", "description": "event_id stamped on the test envelope." }, "delivery_id": { "type": "string", "format": "uuid" }, "success": { "type": "boolean" }, "status_code": { "type": [ "integer", "null" ] }, "response_time_ms": { "type": [ "integer", "null" ] }, "response_body": { "type": [ "string", "null" ] }, "error_message": { "type": [ "string", "null" ] }, "tested_at": { "type": "string", "format": "date-time" } }, "required": [ "test_id", "delivery_id", "success", "tested_at" ] }, "WebhookSecretRotationV1": { "type": "object", "properties": { "webhook_id": { "type": "string", "format": "uuid" }, "signing_secret": { "type": "string", "description": "32-byte base64url secret; only returned at rotation time." } }, "required": [ "webhook_id", "signing_secret" ] }, "WebhookSubscriptionsResponseV1": { "type": "object", "properties": { "webhook_id": { "type": "string", "format": "uuid" }, "event_types": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookEventTypeV1" } } }, "required": [ "webhook_id", "event_types" ] }, "WebhookSignatureValidationV1": { "type": "object", "properties": { "valid": { "type": "boolean" } }, "required": [ "valid" ] }, "WebhookEventListV1": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookEventTypeV1" } }, "WebhookAnalyticsResponseV1": { "type": "object", "properties": { "webhook_id": { "type": "string", "format": "uuid" }, "date_from": { "type": "string", "format": "date-time" }, "date_to": { "type": "string", "format": "date-time" }, "metrics": { "type": "object", "properties": { "total_deliveries": { "type": "integer" }, "successful_deliveries": { "type": "integer" }, "failed_deliveries": { "type": "integer" }, "success_rate": { "type": "number" }, "average_response_time": { "type": "number" }, "deliveries_by_status": { "type": "object", "additionalProperties": { "type": "integer" } }, "deliveries_by_event_type": { "type": "object", "additionalProperties": { "type": "integer" } }, "deliveries_timeline": { "type": "array", "items": { "type": "object", "properties": { "date": { "type": "string" }, "successful": { "type": "integer" }, "failed": { "type": "integer" } }, "required": [ "date", "successful", "failed" ] } }, "response_time_percentiles": { "type": "object", "properties": { "p50": { "type": "number" }, "p90": { "type": "number" }, "p95": { "type": "number" }, "p99": { "type": "number" } }, "required": [ "p50", "p90", "p95", "p99" ] } }, "required": [ "total_deliveries", "successful_deliveries", "failed_deliveries", "success_rate", "average_response_time", "deliveries_by_status", "deliveries_by_event_type", "deliveries_timeline" ] } }, "required": [ "date_from", "date_to", "metrics" ] }, "WebhookTemplateRecordV1": { "type": "object", "properties": { "template_id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "description": { "type": "string" }, "category": { "type": "string" }, "default_config": { "type": "object", "properties": { "url_template": { "type": "string" }, "method": { "type": "string", "enum": [ "GET", "POST", "PUT", "PATCH", "DELETE" ] }, "headers": { "type": "object", "additionalProperties": { "type": "string" } }, "payload_template": { "type": "string" }, "security_type": { "type": "string", "enum": [ "none", "basic_auth", "bearer_token", "api_key", "hmac_signature", "oauth2" ] } }, "required": [ "method", "payload_template" ] }, "required_fields": { "type": "array", "items": { "type": "string" } }, "supported_events": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookEventTypeV1" } }, "is_system_template": { "type": "boolean" }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" }, "tenant": { "type": [ "string", "null" ], "format": "uuid" } }, "required": [ "template_id", "name", "category", "default_config", "created_at", "updated_at" ] }, "WebhookListResponseV1": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookRecordV1" } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" }, "hasNext": { "type": "boolean" }, "hasPrev": { "type": "boolean" } }, "required": [ "page", "limit", "total", "totalPages", "hasNext", "hasPrev" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data", "pagination" ] }, "WebhookDeliveryListResponseV1": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookDeliveryRecordV1" } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" }, "hasNext": { "type": "boolean" }, "hasPrev": { "type": "boolean" } }, "required": [ "page", "limit", "total", "totalPages", "hasNext", "hasPrev" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data", "pagination" ] }, "WebhookEnvelopeV1": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/WebhookRecordV1" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "WebhookDeliveryEnvelopeV1": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/WebhookDeliveryRecordV1" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "WebhookHealthEnvelopeV1": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/WebhookHealthV1" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "WebhookTestResultEnvelopeV1": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/WebhookTestResultV1" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "WebhookSecretRotationEnvelopeV1": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/WebhookSecretRotationV1" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "WebhookSubscriptionsEnvelopeV1": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/WebhookSubscriptionsResponseV1" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "WebhookSignatureValidationEnvelopeV1": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/WebhookSignatureValidationV1" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "WebhookEventListEnvelopeV1": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/WebhookEventListV1" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "WebhookAnalyticsEnvelopeV1": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/WebhookAnalyticsResponseV1" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "WebhookTemplateListEnvelopeV1": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookTemplateRecordV1" } }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "WebhookTemplateEnvelopeV1": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/WebhookTemplateRecordV1" }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "TicketWebhookChangeEntryV1": { "type": "object", "properties": { "previous": {}, "new": {} } }, "TicketWebhookCommentBlockV1": { "type": "object", "properties": { "text": { "type": "string" }, "author": { "type": [ "string", "null" ] }, "timestamp": { "type": "string", "format": "date-time" }, "is_internal": { "type": "boolean" } }, "required": [ "text", "author", "timestamp", "is_internal" ] }, "TicketWebhookCommentsEntryV1": { "type": "object", "properties": { "comment_id": { "type": "string", "format": "uuid" }, "text": { "type": "string" }, "author": { "type": [ "string", "null" ] }, "is_internal": { "type": "boolean" }, "is_resolution": { "type": "boolean" }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": [ "string", "null" ], "format": "date-time" } }, "required": [ "comment_id", "text", "author", "is_internal", "is_resolution", "created_at", "updated_at" ] }, "TicketWebhookDataV1": { "type": "object", "properties": { "ticket_id": { "type": "string", "format": "uuid", "description": "Always present; the correlation key." }, "ticket_number": { "type": [ "string", "null" ] }, "title": { "type": [ "string", "null" ] }, "url": { "type": "string", "format": "uri" }, "status_id": { "type": [ "string", "null" ], "format": "uuid" }, "status_name": { "type": [ "string", "null" ] }, "is_closed": { "type": "boolean" }, "previous_status_id": { "type": [ "string", "null" ], "format": "uuid", "description": "Only populated on ticket.status_changed." }, "previous_status_name": { "type": [ "string", "null" ], "description": "Only populated on ticket.status_changed." }, "priority_id": { "type": [ "string", "null" ], "format": "uuid" }, "priority_name": { "type": [ "string", "null" ] }, "client_id": { "type": [ "string", "null" ], "format": "uuid" }, "client_name": { "type": [ "string", "null" ] }, "contact_name_id": { "type": [ "string", "null" ], "format": "uuid" }, "contact_name": { "type": [ "string", "null" ] }, "contact_email": { "type": [ "string", "null" ] }, "assigned_to": { "type": [ "string", "null" ], "format": "uuid" }, "assigned_to_name": { "type": [ "string", "null" ] }, "assigned_team_id": { "type": [ "string", "null" ], "format": "uuid" }, "board_id": { "type": [ "string", "null" ], "format": "uuid" }, "board_name": { "type": [ "string", "null" ] }, "category_id": { "type": [ "string", "null" ], "format": "uuid" }, "subcategory_id": { "type": [ "string", "null" ], "format": "uuid" }, "entered_at": { "type": [ "string", "null" ], "format": "date-time" }, "updated_at": { "type": [ "string", "null" ], "format": "date-time" }, "closed_at": { "type": [ "string", "null" ], "format": "date-time" }, "due_date": { "type": [ "string", "null" ] }, "tags": { "type": "array", "items": { "type": "string" } }, "changes": { "type": "object", "additionalProperties": { "$ref": "#/components/schemas/TicketWebhookChangeEntryV1" }, "description": "Field-level diff. Only on ticket.updated." }, "comment": { "allOf": [ { "$ref": "#/components/schemas/TicketWebhookCommentBlockV1" }, { "description": "Newly-added comment. Only on ticket.comment.added. Attachments are never included." } ] }, "comments": { "type": "array", "items": { "$ref": "#/components/schemas/TicketWebhookCommentsEntryV1" }, "description": "Full comment thread, oldest first. Only included when the webhook subscription opted into the `comments` field." } }, "required": [ "ticket_id" ] }, "WebhookOutboundHeadersV1": { "type": "object", "properties": { "x-alga-signature": { "type": "string", "description": "`t=,v1=` over `${timestamp}.${raw_body}`." }, "x-alga-webhook-id": { "type": "string", "format": "uuid" }, "x-alga-event-id": { "type": "string", "format": "uuid" }, "x-alga-event-type": { "$ref": "#/components/schemas/WebhookEventTypeV1" }, "x-alga-delivery-id": { "type": "string", "format": "uuid" }, "x-alga-delivery-attempt": { "type": "string", "description": "Stringified attempt count, starts at 1." } }, "required": [ "x-alga-signature", "x-alga-webhook-id", "x-alga-event-id", "x-alga-event-type", "x-alga-delivery-id", "x-alga-delivery-attempt" ] }, "TicketCreatedDeliveryV1": { "type": "object", "properties": { "event_id": { "type": "string", "format": "uuid" }, "event_type": { "type": "string", "enum": [ "ticket.created" ] }, "occurred_at": { "type": "string", "format": "date-time" }, "tenant_id": { "type": "string", "format": "uuid" }, "data": { "$ref": "#/components/schemas/TicketWebhookDataV1" } }, "required": [ "event_id", "event_type", "occurred_at", "tenant_id", "data" ] }, "TicketUpdatedDeliveryV1": { "type": "object", "properties": { "event_id": { "type": "string", "format": "uuid" }, "event_type": { "type": "string", "enum": [ "ticket.updated" ] }, "occurred_at": { "type": "string", "format": "date-time" }, "tenant_id": { "type": "string", "format": "uuid" }, "data": { "$ref": "#/components/schemas/TicketWebhookDataV1" } }, "required": [ "event_id", "event_type", "occurred_at", "tenant_id", "data" ] }, "TicketStatusChangedDeliveryV1": { "type": "object", "properties": { "event_id": { "type": "string", "format": "uuid" }, "event_type": { "type": "string", "enum": [ "ticket.status_changed" ] }, "occurred_at": { "type": "string", "format": "date-time" }, "tenant_id": { "type": "string", "format": "uuid" }, "data": { "$ref": "#/components/schemas/TicketWebhookDataV1" } }, "required": [ "event_id", "event_type", "occurred_at", "tenant_id", "data" ] }, "TicketAssignedDeliveryV1": { "type": "object", "properties": { "event_id": { "type": "string", "format": "uuid" }, "event_type": { "type": "string", "enum": [ "ticket.assigned" ] }, "occurred_at": { "type": "string", "format": "date-time" }, "tenant_id": { "type": "string", "format": "uuid" }, "data": { "$ref": "#/components/schemas/TicketWebhookDataV1" } }, "required": [ "event_id", "event_type", "occurred_at", "tenant_id", "data" ] }, "TicketClosedDeliveryV1": { "type": "object", "properties": { "event_id": { "type": "string", "format": "uuid" }, "event_type": { "type": "string", "enum": [ "ticket.closed" ] }, "occurred_at": { "type": "string", "format": "date-time" }, "tenant_id": { "type": "string", "format": "uuid" }, "data": { "$ref": "#/components/schemas/TicketWebhookDataV1" } }, "required": [ "event_id", "event_type", "occurred_at", "tenant_id", "data" ] }, "TicketCommentAddedDeliveryV1": { "type": "object", "properties": { "event_id": { "type": "string", "format": "uuid" }, "event_type": { "type": "string", "enum": [ "ticket.comment.added" ] }, "occurred_at": { "type": "string", "format": "date-time" }, "tenant_id": { "type": "string", "format": "uuid" }, "data": { "$ref": "#/components/schemas/TicketWebhookDataV1" } }, "required": [ "event_id", "event_type", "occurred_at", "tenant_id", "data" ] }, "WorkflowV1MissingApiError": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] }, "WorkflowV1MissingRouteResponse": { "type": "string", "description": "Default Next.js not-found response body for missing route handlers." }, "ProjectIdParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Project UUID." } }, "required": [ "id" ] }, "ProjectPhaseTaskParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Project UUID." }, "phaseId": { "type": "string", "format": "uuid", "description": "Project phase UUID." } }, "required": [ "id", "phaseId" ] }, "ProjectTaskIdParams": { "type": "object", "properties": { "taskId": { "type": "string", "format": "uuid", "description": "Project task UUID." } }, "required": [ "taskId" ] }, "ProjectTaskApiResponse": { "type": "object", "properties": { "task_id": { "type": "string", "format": "uuid" }, "phase_id": { "type": "string", "format": "uuid" }, "task_name": { "type": "string" }, "description": { "type": [ "string", "null" ] }, "assigned_to": { "type": [ "string", "null" ], "format": "uuid" }, "estimated_hours": { "type": [ "number", "null" ] }, "actual_hours": { "type": [ "number", "null" ] }, "project_status_mapping_id": { "type": "string", "format": "uuid" }, "due_date": { "type": [ "string", "null" ], "format": "date-time" }, "priority_id": { "type": [ "string", "null" ], "format": "uuid" }, "task_type_key": { "type": "string" }, "wbs_code": { "type": "string" }, "order_key": { "type": "string" }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" }, "tenant": { "type": "string", "format": "uuid" }, "tags": { "type": "array", "items": { "type": "string" } } }, "required": [ "task_id", "phase_id", "task_name", "description", "assigned_to", "estimated_hours", "actual_hours", "project_status_mapping_id", "priority_id", "task_type_key", "wbs_code", "created_at", "updated_at", "tenant" ] }, "ProjectTaskEnvelope": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/ProjectTaskApiResponse" } }, "required": [ "data" ] }, "ProjectTaskListEnvelope": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/ProjectTaskApiResponse" } } }, "required": [ "data" ] }, "ProjectTaskStatusMappingApiResponse": { "type": "object", "properties": { "project_status_mapping_id": { "type": "string", "format": "uuid" }, "project_id": { "type": "string", "format": "uuid" }, "status_id": { "type": [ "string", "null" ], "format": "uuid" }, "standard_status_id": { "type": [ "string", "null" ], "format": "uuid" }, "is_standard": { "type": "boolean" }, "custom_name": { "type": [ "string", "null" ] }, "display_order": { "type": "number" }, "is_visible": { "type": "boolean" }, "tenant": { "type": "string", "format": "uuid" }, "status_name": { "type": "string" }, "name": { "type": "string" }, "is_closed": { "type": "boolean" } }, "required": [ "project_status_mapping_id", "project_id", "is_standard", "custom_name", "display_order", "is_visible", "tenant", "status_name", "name", "is_closed" ] }, "ProjectTaskStatusMappingListEnvelope": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/ProjectTaskStatusMappingApiResponse" } } }, "required": [ "data" ] }, "ProjectTaskUpdateRequest": { "type": "object", "properties": { "task_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "description": { "type": "string" }, "assigned_to": { "type": "string", "format": "uuid" }, "estimated_hours": { "type": "number", "minimum": 0 }, "due_date": { "type": "string", "format": "date-time" }, "priority_id": { "type": "string", "format": "uuid" }, "task_type_key": { "type": "string", "default": "general" }, "project_status_mapping_id": { "type": "string", "format": "uuid" }, "wbs_code": { "type": "string" }, "tags": { "type": "array", "items": { "type": "string" } } }, "description": "Payload for updating a project task. To change task status, send project_status_mapping_id as a UUID. This endpoint does not accept project_id, phase_id, or human-readable status names." }, "ProjectTaskCreateRequest": { "type": "object", "properties": { "task_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "description": { "type": "string" }, "assigned_to": { "type": "string", "format": "uuid" }, "estimated_hours": { "type": "number", "minimum": 0 }, "due_date": { "type": "string", "format": "date-time" }, "priority_id": { "type": "string", "format": "uuid" }, "task_type_key": { "type": "string", "default": "general" }, "project_status_mapping_id": { "type": "string", "format": "uuid" }, "wbs_code": { "type": "string" }, "tags": { "type": "array", "items": { "type": "string" } } }, "required": [ "task_name", "project_status_mapping_id" ], "description": "Payload for creating a project task within a phase. Provide project_status_mapping_id as a UUID from the project task status mappings endpoint. The phaseId path parameter selects which phase will receive the task." }, "WorkV1IdParam": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." } }, "required": [ "id" ] }, "WorkV1ProjectPhaseParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Project UUID from projects.project_id." }, "phaseId": { "type": "string", "format": "uuid", "description": "Project phase UUID from project_phases.phase_id." } }, "required": [ "id", "phaseId" ] }, "WorkV1ProjectTaskParam": { "type": "object", "properties": { "taskId": { "type": "string", "format": "uuid", "description": "Project task UUID from project_tasks.task_id." } }, "required": [ "taskId" ] }, "WorkV1SessionParam": { "type": "object", "properties": { "sessionId": { "type": "string", "description": "Active time-tracking session identifier." } }, "required": [ "sessionId" ] }, "WorkV1TagEntityParams": { "type": "object", "properties": { "entityType": { "type": "string", "description": "Tagged entity type from route segment." }, "entityId": { "type": "string", "description": "Tagged entity id from route segment." } }, "required": [ "entityType", "entityId" ] }, "WorkV1ListQuery": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] }, "fields": { "type": "string" }, "search": { "type": "string" }, "query": { "type": "string" } } }, "WorkV1GenericBody": { "type": "object", "additionalProperties": {}, "description": "Controller/service-specific payload; see source route/controller for exact required shape." }, "WorkV1CreateProjectBody": { "type": "object", "properties": { "project_name": { "type": "string" }, "status": { "type": "string" }, "start_date": { "type": "string" }, "end_date": { "type": "string" }, "assigned_to": { "type": "string", "format": "uuid" }, "assigned_team_id": { "type": "string", "format": "uuid" }, "client_id": { "type": "string", "format": "uuid" } }, "required": [ "project_name" ] }, "WorkV1CreateTicketBody": { "type": "object", "properties": { "title": { "type": "string" }, "summary": { "type": "string" }, "client_id": { "type": "string", "format": "uuid" }, "board_id": { "type": "string", "format": "uuid" }, "priority_id": { "type": "string", "format": "uuid" }, "status_id": { "type": "string", "format": "uuid" } } }, "WorkV1CreateTagBody": { "type": "object", "properties": { "tag_text": { "type": "string" }, "text": { "type": "string" }, "color": { "type": "string" }, "background_color": { "type": "string" } }, "required": [ "tag_text" ] }, "WorkV1CreateTimeEntryBody": { "type": "object", "properties": { "work_item_type": { "type": "string" }, "work_item_id": { "type": "string" }, "user_id": { "type": "string", "format": "uuid" }, "started_at": { "type": "string" }, "ended_at": { "type": "string" }, "duration_minutes": { "type": "number" }, "billable_minutes": { "type": "number" }, "notes": { "type": "string" } } }, "WorkV1CreateTimeSheetBody": { "type": "object", "properties": { "user_id": { "type": "string", "format": "uuid" }, "period_id": { "type": "string", "format": "uuid" }, "status": { "type": "string" }, "notes": { "type": "string" } } }, "WorkV1ApiError": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] }, "WorkV1ApiSuccess": { "type": "object", "properties": { "data": { "anyOf": [ { "type": "object", "additionalProperties": {} }, { "type": "array", "items": { "type": "object", "additionalProperties": {} } } ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "WorkV1ApiPaginated": { "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object", "additionalProperties": {} } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" }, "hasNext": { "type": "boolean" }, "hasPrev": { "type": "boolean" } }, "required": [ "page", "limit", "total", "totalPages", "hasNext", "hasPrev" ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data", "pagination" ] }, "WorkV1TicketCommentParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Ticket UUID." }, "commentId": { "type": "string", "format": "uuid", "description": "Comment UUID." } }, "required": [ "id", "commentId" ] }, "WorkV1TicketDocumentParams": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Ticket UUID." }, "documentId": { "type": "string", "format": "uuid", "description": "Document UUID." } }, "required": [ "id", "documentId" ] }, "WorkV1TicketStatusesQuery": { "type": "object", "properties": { "board_id": { "type": "string", "format": "uuid", "description": "Filter statuses to this board." } } }, "WorkV1TicketCommentUpdateBody": { "type": "object", "properties": { "comment_text": { "type": "string", "minLength": 1, "maxLength": 5000, "description": "Updated comment text (1–5000 visible chars)." } }, "required": [ "comment_text" ] }, "WorkV1TicketReactionBody": { "type": "object", "properties": { "emoji": { "type": "string", "description": "Emoji to toggle on the comment." } }, "required": [ "emoji" ] }, "WorkV1TicketMaterialBody": { "type": "object", "properties": { "service_id": { "type": "string", "format": "uuid" }, "quantity": { "type": "number", "exclusiveMinimum": 0 }, "rate": { "type": "number", "minimum": 0 }, "currency_code": { "type": "string", "minLength": 3, "maxLength": 3 }, "description": { "type": [ "string", "null" ], "maxLength": 1000 } }, "required": [ "service_id", "quantity", "rate", "currency_code" ] }, "QuoteIdParamV1": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." } }, "required": [ "id" ] }, "QuoteItemParamsV1": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "itemId": { "type": "string", "format": "uuid", "description": "Quote item UUID from quote_items.quote_item_id." } }, "required": [ "id", "itemId" ] }, "ContractLineParamsV1": { "type": "object", "properties": { "contractId": { "type": "string", "format": "uuid", "description": "Contract UUID from contracts.contract_id." }, "contractLineId": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." } }, "required": [ "contractId", "contractLineId" ] }, "ContractIdParamV1": { "type": "object", "properties": { "contractId": { "type": "string", "format": "uuid", "description": "Contract UUID from contracts.contract_id." } }, "required": [ "contractId" ] }, "QuotesContractsListQueryV1": { "type": "object", "properties": { "page": { "type": "string" }, "limit": { "type": "string" }, "sort": { "type": "string" }, "order": { "type": "string", "enum": [ "asc", "desc" ] }, "search": { "type": "string" }, "status": { "type": "string" }, "include_items": { "type": "string", "enum": [ "true", "false" ] }, "include_client": { "type": "string", "enum": [ "true", "false" ] } } }, "QuotesContractsGenericBodyV1": { "type": "object", "additionalProperties": {}, "description": "Controller/service-specific payload; see quote/contract controller schemas for exact required fields." }, "CreateQuoteBodyV1": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "title": { "type": "string" }, "quote_number": { "type": "string" }, "valid_until": { "type": "string" }, "notes": { "type": "string" }, "terms": { "type": "string" }, "items": { "type": "array", "items": { "type": "object", "additionalProperties": {} } } }, "required": [ "client_id" ] }, "CreateContractBodyV1": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, "contract_name": { "type": "string" }, "start_date": { "type": "string" }, "end_date": { "type": "string" }, "billing_frequency": { "type": "string" } }, "required": [ "client_id" ] }, "QuotesContractsApiErrorV1": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] }, "QuotesContractsApiSuccessV1": { "type": "object", "properties": { "data": { "anyOf": [ { "type": "object", "additionalProperties": {} }, { "type": "array", "items": { "type": "object", "additionalProperties": {} } } ] }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "QuotesContractsApiPaginatedV1": { "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object", "additionalProperties": {} } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" } }, "required": [ "page", "limit", "total", "totalPages" ] }, "meta": { "type": "object", "additionalProperties": {} }, "_links": { "type": "object", "additionalProperties": {} } }, "required": [ "data" ] }, "FeatureFlagsPostBodyV1": { "type": "object", "properties": { "flags": { "type": "array", "items": { "type": "string" } }, "context": { "type": "object", "properties": { "userRole": { "type": "string" }, "companySize": { "type": "string", "enum": [ "small", "medium", "large", "enterprise" ] }, "subscriptionPlan": { "type": "string" }, "customProperties": { "type": "object", "additionalProperties": {} } } } } }, "FeatureAccessBodyV1": { "type": "object", "properties": { "user_id": { "type": "string", "format": "uuid" }, "feature_name": { "type": "string" } }, "required": [ "feature_name" ] }, "MetaUtilityApiErrorV1": { "type": "object", "properties": { "error": {}, "message": { "type": "string" }, "details": {}, "success": { "type": "boolean" } } }, "MetaUtilityApiSuccessV1": { "type": "object", "properties": { "data": { "anyOf": [ { "type": "object", "additionalProperties": {} }, { "type": "array", "items": { "type": "object", "additionalProperties": {} } }, { "type": "string" }, { "type": "number" }, { "type": "boolean" }, { "type": "null" } ] }, "flags": { "type": "object", "additionalProperties": {} }, "context": { "type": "object", "additionalProperties": {} }, "enabled": { "type": "boolean" }, "reason": { "type": "string" }, "usageStatsEnabled": { "type": "boolean" }, "controlledBy": { "type": "string" }, "message": { "type": "string" }, "success": { "type": "boolean" } } }, "EeInventoryApiError": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] }, "EeInventoryMissingRouteResponse": { "type": "string", "description": "Default Next.js not-found response body for missing route handlers." }, "MiddlewareUnauthorizedResponse": { "type": "object", "properties": { "error": { "type": "string", "description": "Middleware authentication error message, such as Unauthorized: API key missing." } }, "required": [ "error" ] }, "SoftwareOneAgreement": { "type": "object", "properties": { "id": { "type": "string", "description": "SoftwareOne agreement identifier, such as agr-001. This is an external SoftwareOne-style ID, not an Alga UUID." }, "name": { "type": "string", "description": "Human-readable agreement name." }, "product": { "type": "string", "description": "Product or SKU name associated with the agreement." }, "vendor": { "type": "string", "description": "Software vendor name, such as Microsoft or Adobe." }, "consumer": { "type": "string", "description": "End-customer organization name for the agreement." }, "status": { "type": "string", "enum": [ "active", "inactive", "pending", "expired" ], "description": "Agreement status." }, "currency": { "type": "string", "description": "Three-letter billing currency code, such as USD." }, "spxy": { "type": "number", "description": "Annual SPx value from SoftwareOne dummy data." }, "marginRpxy": { "type": "number", "description": "Margin RPxY value from SoftwareOne dummy data." }, "operations": { "type": "string", "enum": [ "visible", "hidden", "restricted" ], "description": "Operational visibility state for the agreement." }, "billingConfigId": { "type": "string", "description": "SoftwareOne billing configuration identifier associated with the agreement." }, "localConfig": { "type": "object", "properties": { "autoRenewal": { "type": "boolean", "description": "Whether local auto-renewal is enabled." }, "notificationDays": { "type": "integer", "description": "Days before renewal/expiry to notify." }, "markup": { "type": "number", "description": "Optional local markup percentage used by richer SoftwareOne schemas." }, "notes": { "type": "string", "description": "Optional local notes." }, "tags": { "type": "array", "items": { "type": "string" }, "description": "Optional local tags." } }, "description": "Locally editable configuration metadata. Current MVP data includes autoRenewal and notificationDays." } }, "required": [ "id", "name", "product", "vendor", "consumer", "status", "currency", "spxy", "marginRpxy", "operations", "billingConfigId" ] }, "SoftwareOneAgreementDetail": { "allOf": [ { "$ref": "#/components/schemas/SoftwareOneAgreement" }, { "type": "object", "properties": { "startDate": { "type": "string", "description": "Agreement start date in YYYY-MM-DD format." }, "endDate": { "type": "string", "description": "Agreement end date in YYYY-MM-DD format." }, "billingCycle": { "type": "string", "description": "Billing cycle cadence, such as monthly or annual." }, "paymentTerms": { "type": "string", "description": "Payment terms, such as Net 30." }, "description": { "type": "string", "description": "Free-text agreement description." }, "contactPerson": { "type": "string", "description": "Primary contact name for the agreement." }, "contactEmail": { "type": "string", "format": "email", "description": "Primary contact email address." }, "licenseCount": { "type": "integer", "description": "Number of licenses covered by the agreement." }, "pricePerLicense": { "type": "number", "description": "Price per license in the agreement currency." } } } ] }, "SoftwareOneStatement": { "type": "object", "properties": { "id": { "type": "string", "description": "SoftwareOne statement identifier, such as stmt-001. This is an external SoftwareOne-style ID, not an Alga UUID." }, "statementNumber": { "type": "string", "description": "Human-readable statement number, such as STMT-2024-001." }, "period": { "type": "string", "description": "Billing period in YYYY-MM format." }, "consumer": { "type": "string", "description": "End-customer organization name." }, "consumerId": { "type": "string", "description": "SoftwareOne consumer identifier." }, "agreementName": { "type": "string", "description": "Human-readable name of the associated agreement." }, "agreementId": { "type": "string", "description": "SoftwareOne agreement identifier associated with this statement." }, "totalAmount": { "type": "number", "description": "Total statement amount in the billing currency." }, "currency": { "type": "string", "description": "Three-letter billing currency code, such as USD." }, "lineItemCount": { "type": "integer", "description": "Number of line-item charges on the statement." }, "status": { "type": "string", "enum": [ "draft", "finalized", "imported" ], "description": "Statement lifecycle status used by the MVP handler." }, "dueDate": { "type": "string", "description": "Payment due date in YYYY-MM-DD format." }, "createdAt": { "type": "string", "description": "ISO 8601 timestamp when the statement was created." }, "importedAt": { "type": [ "string", "null" ], "description": "ISO 8601 timestamp when imported into Alga, or null if not imported." }, "subtotal": { "type": "number", "description": "Statement subtotal before tax. Present on detail responses." }, "taxAmount": { "type": "number", "description": "Statement tax amount. Present on detail responses." }, "description": { "type": "string", "description": "Statement description. Present on detail responses." }, "billingAddress": { "type": "object", "properties": { "client": { "type": "string", "description": "Billing client organization name." }, "street": { "type": "string", "description": "Street address." }, "city": { "type": "string", "description": "City." }, "state": { "type": "string", "description": "State or province." }, "zipCode": { "type": "string", "description": "Postal or ZIP code." }, "country": { "type": "string", "description": "Country." } }, "required": [ "client", "street", "city", "state", "zipCode", "country" ], "description": "Billing address. Present on detail responses." } }, "required": [ "id", "statementNumber", "period", "consumer", "consumerId", "agreementName", "agreementId", "totalAmount", "currency", "lineItemCount", "status", "dueDate", "createdAt", "importedAt" ] }, "SoftwareOnePaginationMeta": { "type": "object", "properties": { "total": { "type": "integer", "description": "Total records returned by the current dummy implementation." }, "page": { "type": "integer", "description": "Current page number. Currently hardcoded to 1." }, "pageSize": { "type": "integer", "description": "Page size. Currently hardcoded to 50." } }, "required": [ "total", "page", "pageSize" ] }, "SoftwareOneAgreementsListResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Request succeeded." }, "data": { "type": "array", "items": { "$ref": "#/components/schemas/SoftwareOneAgreement" }, "description": "SoftwareOne agreement records." }, "meta": { "$ref": "#/components/schemas/SoftwareOnePaginationMeta" } }, "required": [ "success", "data", "meta" ] }, "SoftwareOneAgreementDetailResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Request succeeded." }, "data": { "$ref": "#/components/schemas/SoftwareOneAgreementDetail" } }, "required": [ "success", "data" ] }, "SoftwareOneStatementsListResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Request succeeded." }, "data": { "type": "array", "items": { "$ref": "#/components/schemas/SoftwareOneStatement" }, "description": "SoftwareOne statement records." }, "meta": { "$ref": "#/components/schemas/SoftwareOnePaginationMeta" } }, "required": [ "success", "data", "meta" ] }, "SoftwareOneStatementDetailResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Request succeeded." }, "data": { "$ref": "#/components/schemas/SoftwareOneStatement" } }, "required": [ "success", "data" ] }, "SoftwareOneCharge": { "type": "object", "properties": { "id": { "type": "string", "description": "SoftwareOne charge identifier, unique within the statement, such as 1-1." }, "statementId": { "type": "string", "description": "Parent SoftwareOne statement identifier, such as stmt-001." }, "description": { "type": "string", "description": "Human-readable line item description." }, "product": { "type": "string", "description": "Product or SKU for the charge." }, "quantity": { "type": "number", "description": "Quantity billed for this line item." }, "unitPrice": { "type": "number", "description": "Unit price in the statement currency." }, "totalAmount": { "type": "number", "description": "Total line amount." }, "agreementId": { "type": "string", "description": "Related SoftwareOne agreement identifier, such as agr-001." }, "period": { "type": "string", "description": "Billing period in YYYY-MM format." }, "category": { "type": "string", "description": "Charge category, such as Software License, Compute, Storage, or Support." } }, "required": [ "id", "statementId", "description", "product", "quantity", "unitPrice", "totalAmount", "agreementId", "period", "category" ] }, "SoftwareOneChargesListResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Request succeeded." }, "data": { "type": "array", "items": { "$ref": "#/components/schemas/SoftwareOneCharge" }, "description": "Charge line items for the requested statement. Unknown statement IDs return an empty array." }, "meta": { "type": "object", "properties": { "total": { "type": "integer", "description": "Number of charge records returned." }, "statementId": { "type": "string", "description": "Statement ID from the path parameter." } }, "required": [ "total", "statementId" ] } }, "required": [ "success", "data", "meta" ] }, "SoftwareOneFullSyncResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Sync request completed successfully." }, "message": { "type": "string", "description": "Human-readable sync result message." }, "data": { "type": "object", "properties": { "agreementsCount": { "type": "integer", "description": "Number of agreements synced. Zero when syncAgreements is false." }, "statementsCount": { "type": "integer", "description": "Number of statements synced. Zero when syncStatements is false." }, "syncedAt": { "type": "string", "format": "date-time", "description": "ISO timestamp when the dummy sync completed." } }, "required": [ "agreementsCount", "statementsCount", "syncedAt" ] } }, "required": [ "success", "message", "data" ] }, "ExtensionIdParams": { "type": "object", "properties": { "extensionId": { "type": "string", "description": "Extension identifier from the URL. The MVP generic routes accept any value and echo it in response metadata." } }, "required": [ "extensionId" ] }, "ExtensionScopedIdParams": { "type": "object", "properties": { "extensionId": { "type": "string", "description": "Extension identifier from the URL. The MVP generic routes accept any value and echo it in response metadata." }, "id": { "type": "string", "description": "SoftwareOne-style external record identifier, such as agr-001 or stmt-001. This is not an Alga UUID." } }, "required": [ "extensionId", "id" ] }, "GenericExtensionAgreementsListResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Request succeeded." }, "data": { "type": "array", "items": { "$ref": "#/components/schemas/SoftwareOneAgreement" }, "description": "Agreement records returned by the generic extension MVP handler." }, "meta": { "type": "object", "properties": { "total": { "type": "integer", "description": "Number of agreements returned." }, "extensionId": { "type": "string", "description": "Extension ID echoed from the path." } }, "required": [ "total", "extensionId" ] } }, "required": [ "success", "data", "meta" ] }, "GenericExtensionAgreementDetailResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Request succeeded." }, "data": { "$ref": "#/components/schemas/SoftwareOneAgreementDetail" }, "meta": { "type": "object", "properties": { "extensionId": { "type": "string", "description": "Extension ID echoed from the path." } }, "required": [ "extensionId" ] } }, "required": [ "success", "data", "meta" ] }, "GenericExtensionStatementsListResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Request succeeded." }, "data": { "type": "array", "items": { "$ref": "#/components/schemas/SoftwareOneStatement" }, "description": "Statement records returned by the generic extension MVP handler." }, "meta": { "type": "object", "properties": { "total": { "type": "integer", "description": "Number of statements returned." }, "extensionId": { "type": "string", "description": "Extension ID echoed from the path." } }, "required": [ "total", "extensionId" ] } }, "required": [ "success", "data", "meta" ] }, "GenericExtensionStatementDetailResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Request succeeded." }, "data": { "$ref": "#/components/schemas/SoftwareOneStatement" }, "meta": { "type": "object", "properties": { "extensionId": { "type": "string", "description": "Extension ID echoed from the path." } }, "required": [ "extensionId" ] } }, "required": [ "success", "data", "meta" ] }, "GenericExtensionChargesListResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Request succeeded." }, "data": { "type": "array", "items": { "$ref": "#/components/schemas/SoftwareOneCharge" }, "description": "Charge line items. Unknown statement IDs return an empty array." }, "meta": { "type": "object", "properties": { "total": { "type": "integer", "description": "Number of charge records returned." }, "statementId": { "type": "string", "description": "Statement ID from the path parameter." }, "extensionId": { "type": "string", "description": "Extension ID echoed from the path." } }, "required": [ "total", "statementId", "extensionId" ] } }, "required": [ "success", "data", "meta" ] }, "GenericExtensionSyncResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Sync request completed successfully." }, "message": { "type": "string", "description": "Human-readable sync result message." }, "data": { "type": "object", "properties": { "agreementsCount": { "type": "integer", "description": "Number of agreements synced. Zero when syncAgreements is false." }, "statementsCount": { "type": "integer", "description": "Number of statements synced. Zero when syncStatements is false." }, "syncedAt": { "type": "string", "format": "date-time", "description": "ISO timestamp when the dummy sync completed." }, "extensionId": { "type": "string", "description": "Extension ID echoed from the path." } }, "required": [ "agreementsCount", "statementsCount", "syncedAt", "extensionId" ] } }, "required": [ "success", "message", "data" ] }, "SoftwareOneSyncResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ true ], "description": "Sync request completed successfully." }, "message": { "type": "string", "description": "Human-readable sync result message." }, "count": { "type": "integer", "description": "Number of dummy records included in the sync result." } }, "required": [ "success", "message", "count" ] }, "SoftwareOneSyncRequest": { "type": "object", "properties": { "fullSync": { "type": "boolean", "description": "Optional future full-sync flag. Currently ignored by the MVP handlers." }, "syncStatements": { "type": "boolean", "description": "Optional future statement sync flag. Currently ignored by the MVP handlers." }, "syncAgreements": { "type": "boolean", "description": "Optional future agreement sync flag. Currently ignored by the MVP handlers." } }, "description": "Sync options. The current SoftwareOne MVP handlers parse JSON but ignore all body fields." }, "SoftwareOneErrorResponse": { "type": "object", "properties": { "success": { "type": "boolean", "enum": [ false ], "description": "Request failed." }, "error": { "type": "string", "description": "Human-readable error message." } }, "required": [ "success", "error" ] }, "SoftwareOneIdParams": { "type": "object", "properties": { "id": { "type": "string", "description": "SoftwareOne external identifier, such as agr-001 or stmt-001. This is not an Alga UUID." } }, "required": [ "id" ] }, "StoragePutRequest": { "type": "object", "properties": { "value": {}, "metadata": { "type": "object", "additionalProperties": {} }, "ttlSeconds": { "type": "integer", "exclusiveMinimum": 0 }, "ifRevision": { "type": "integer", "minimum": 0 }, "schemaVersion": { "type": "integer", "exclusiveMinimum": 0 } } }, "StoragePutResponse": { "type": "object", "properties": { "namespace": { "type": "string" }, "key": { "type": "string" }, "revision": { "type": "integer", "exclusiveMinimum": 0 }, "ttlExpiresAt": { "type": [ "string", "null" ], "format": "date-time" }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } }, "required": [ "namespace", "key", "revision", "ttlExpiresAt", "createdAt", "updatedAt" ] }, "StorageGetResponse": { "type": "object", "properties": { "namespace": { "type": "string" }, "key": { "type": "string" }, "revision": { "type": "integer", "exclusiveMinimum": 0 }, "value": {}, "metadata": { "type": "object", "additionalProperties": {} }, "ttlExpiresAt": { "type": [ "string", "null" ], "format": "date-time" }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } }, "required": [ "namespace", "key", "revision", "metadata", "ttlExpiresAt", "createdAt", "updatedAt" ] }, "StorageListQuery": { "type": "object", "properties": { "limit": { "type": "integer", "minimum": 1, "maximum": 100 }, "cursor": { "type": "string" }, "keyPrefix": { "type": "string", "maxLength": 256 }, "includeValues": { "type": "boolean" }, "includeMetadata": { "type": "boolean" } } }, "StorageListItem": { "type": "object", "properties": { "namespace": { "type": "string" }, "key": { "type": "string" }, "revision": { "type": "integer", "exclusiveMinimum": 0 }, "value": {}, "metadata": { "type": "object", "additionalProperties": {} }, "ttlExpiresAt": { "type": [ "string", "null" ], "format": "date-time" }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } }, "required": [ "namespace", "key", "revision", "ttlExpiresAt", "createdAt", "updatedAt" ] }, "StorageListResponse": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/StorageListItem" } }, "nextCursor": { "type": [ "string", "null" ] } }, "required": [ "items", "nextCursor" ] }, "StorageBulkPutRequest": { "type": "object", "properties": { "items": { "type": "array", "items": { "type": "object", "properties": { "key": { "type": "string", "minLength": 1, "maxLength": 256 }, "value": {}, "metadata": { "type": "object", "additionalProperties": {} }, "ttlSeconds": { "type": "integer", "exclusiveMinimum": 0 }, "ifRevision": { "type": "integer", "minimum": 0 }, "schemaVersion": { "type": "integer", "exclusiveMinimum": 0 } }, "required": [ "key" ] }, "minItems": 1 } }, "required": [ "items" ] }, "StorageBulkPutResponse": { "type": "object", "properties": { "namespace": { "type": "string" }, "items": { "type": "array", "items": { "type": "object", "properties": { "key": { "type": "string" }, "revision": { "type": "integer", "exclusiveMinimum": 0 }, "ttlExpiresAt": { "type": [ "string", "null" ], "format": "date-time" } }, "required": [ "key", "revision", "ttlExpiresAt" ] } } }, "required": [ "namespace", "items" ] }, "StorageNamespaceParams": { "type": "object", "properties": { "namespace": { "type": "string", "minLength": 1, "maxLength": 128 } }, "required": [ "namespace" ] }, "StorageNamespaceKeyParams": { "allOf": [ { "$ref": "#/components/schemas/StorageNamespaceParams" }, { "type": "object", "properties": { "key": { "type": "string", "minLength": 1, "maxLength": 256 } }, "required": [ "key" ] } ] }, "StorageIfRevisionHeader": { "type": "object", "properties": { "if-revision-match": { "type": "string" } } }, "StorageDeleteQuery": { "type": "object", "properties": { "ifRevision": { "type": [ "integer", "null" ], "minimum": 0 } } } }, "parameters": {} }, "paths": { "/api/auth/google/callback": { "get": { "summary": "Handle Google OAuth callback", "description": "Browser popup callback for the Gmail email-provider OAuth flow. Google redirects here with an authorization code and state. The handler decodes the state to find the tenant and email provider, exchanges the code for Google access and refresh tokens, stores them on the provider configuration, marks the provider connected, and may provision Gmail watch/Pub/Sub resources. This endpoint is public and protected by the OAuth state parameter; it always responds with text/html that posts the success or error payload to the opener window rather than returning JSON.", "tags": [ "Auth" ], "security": [], "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "OAuth 2.0 authorization code. Required unless the provider returned error." }, "required": false, "description": "OAuth 2.0 authorization code. Required unless the provider returned error.", "name": "code", "in": "query" }, { "schema": { "type": "string", "description": "Base64-encoded JSON state object generated when the OAuth flow was initiated. It carries tenant context, providerId, redirectUri, timestamp, and nonce values used to complete the callback." }, "required": false, "description": "Base64-encoded JSON state object generated when the OAuth flow was initiated. It carries tenant context, providerId, redirectUri, timestamp, and nonce values used to complete the callback.", "name": "state", "in": "query" }, { "schema": { "type": "string", "description": "OAuth error code returned by the provider, such as access_denied." }, "required": false, "description": "OAuth error code returned by the provider, such as access_denied.", "name": "error", "in": "query" }, { "schema": { "type": "string", "description": "Human-readable OAuth error description returned by the provider." }, "required": false, "description": "Human-readable OAuth error description returned by the provider.", "name": "error_description", "in": "query" } ], "responses": { "200": { "description": "HTML popup callback page. The embedded postMessage payload reports success, provider=google, token expiry, and echoed code/state, or an OAuth/configuration/token-exchange error.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/OAuthCallbackHtmlResponse" } } } } } } }, "/api/auth/microsoft/callback": { "get": { "summary": "Handle Microsoft OAuth callback", "description": "Browser popup callback for the Microsoft Graph email-provider OAuth flow. Microsoft redirects here with an authorization code and state. The handler decodes tenant/provider context from state, resolves the tenant Microsoft profile credentials, exchanges the code for Microsoft Graph tokens, stores them on the email provider, marks the provider connected, and best-effort registers a Graph webhook subscription. This endpoint is public and protected by the OAuth state parameter; it always responds with text/html that posts the success or error payload to the opener window rather than returning JSON.", "tags": [ "Auth" ], "security": [], "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "OAuth 2.0 authorization code. Required unless the provider returned error." }, "required": false, "description": "OAuth 2.0 authorization code. Required unless the provider returned error.", "name": "code", "in": "query" }, { "schema": { "type": "string", "description": "Base64-encoded JSON state object generated when the OAuth flow was initiated. It carries tenant context, providerId, redirectUri, timestamp, and nonce values used to complete the callback." }, "required": false, "description": "Base64-encoded JSON state object generated when the OAuth flow was initiated. It carries tenant context, providerId, redirectUri, timestamp, and nonce values used to complete the callback.", "name": "state", "in": "query" }, { "schema": { "type": "string", "description": "OAuth error code returned by the provider, such as access_denied." }, "required": false, "description": "OAuth error code returned by the provider, such as access_denied.", "name": "error", "in": "query" }, { "schema": { "type": "string", "description": "Human-readable OAuth error description returned by the provider." }, "required": false, "description": "Human-readable OAuth error description returned by the provider.", "name": "error_description", "in": "query" } ], "responses": { "200": { "description": "HTML popup callback page. The embedded postMessage payload reports success, provider=microsoft, token expiry, and echoed code/state, or an OAuth/configuration/token-exchange error.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/OAuthCallbackHtmlResponse" } } } } } } }, "/api/auth/{nextauth}": { "get": { "summary": "Handle NextAuth GET action", "description": "Catch-all Auth.js/NextAuth GET endpoint. The nextauth path segment selects the action: csrf returns a CSRF token and sets the CSRF cookie, providers returns configured provider metadata, session returns the current session or {}, signin/signout/error/verify-request render or redirect to configured pages, and callback/{provider} handles OAuth provider redirects. This route is the authentication surface itself and does not require API-key authentication.", "tags": [ "Auth" ], "security": [], "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "NextAuth catch-all action path. Common values include csrf, providers, signin, signout, session, error, verify-request, webauthn-options, callback/credentials, callback/google, callback/azure-ad, and callback/keycloak." }, "required": true, "description": "NextAuth catch-all action path. Common values include csrf, providers, signin, signout, session, error, verify-request, webauthn-options, callback/credentials, callback/google, callback/azure-ad, and callback/keycloak.", "name": "nextauth", "in": "path" }, { "schema": { "type": "string", "description": "Optional URL to redirect to after sign-in or sign-out flows." }, "required": false, "description": "Optional URL to redirect to after sign-in or sign-out flows.", "name": "callbackUrl", "in": "query" }, { "schema": { "type": "string", "description": "Optional NextAuth/OAuth error code shown by sign-in or error pages." }, "required": false, "description": "Optional NextAuth/OAuth error code shown by sign-in or error pages.", "name": "error", "in": "query" }, { "schema": { "type": "string", "description": "OAuth authorization code for provider callback sub-routes." }, "required": false, "description": "OAuth authorization code for provider callback sub-routes.", "name": "code", "in": "query" }, { "schema": { "type": "string", "description": "OAuth state value for provider callback sub-routes." }, "required": false, "description": "OAuth state value for provider callback sub-routes.", "name": "state", "in": "query" }, { "schema": { "type": "string", "description": "Human-readable provider error description for OAuth callback sub-routes." }, "required": false, "description": "Human-readable provider error description for OAuth callback sub-routes.", "name": "error_description", "in": "query" } ], "responses": { "200": { "description": "Successful JSON or HTML response for the selected NextAuth action. Examples include { csrfToken }, provider maps, session objects, {}, or built-in HTML pages.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NextAuthGetResponse" } } } }, "302": { "description": "Redirect to the configured sign-in, sign-out, callback, or error destination.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RedirectResponse" } } } }, "404": { "description": "No content when a built-in action is unavailable or disabled." } } }, "post": { "summary": "Handle NextAuth POST action", "description": "Catch-all Auth.js/NextAuth POST endpoint. The nextauth path segment selects the action: callback/credentials authenticates email and password credentials, callback/{provider} handles OAuth callback form posts, signout clears the current session, session returns or updates session data, and csrf returns a CSRF token. Mutating actions require the csrfToken body field matching the Auth.js CSRF cookie. Credential success sets the encrypted Auth.js session cookie and redirects; failures typically redirect to /auth/signin with an error code.", "tags": [ "Auth" ], "security": [], "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "NextAuth catch-all action path. Common values include csrf, providers, signin, signout, session, error, verify-request, webauthn-options, callback/credentials, callback/google, callback/azure-ad, and callback/keycloak." }, "required": true, "description": "NextAuth catch-all action path. Common values include csrf, providers, signin, signout, session, error, verify-request, webauthn-options, callback/credentials, callback/google, callback/azure-ad, and callback/keycloak.", "name": "nextauth", "in": "path" } ], "requestBody": { "description": "Form fields consumed by the selected NextAuth POST action.", "required": true, "content": { "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/NextAuthPostBody" }, "description": "Form fields consumed by the selected NextAuth POST action." } } }, "responses": { "200": { "description": "JSON response for session or csrf POST actions.", "content": { "application/json": { "schema": { "anyOf": [ { "$ref": "#/components/schemas/AuthSessionAuthenticatedResponse" }, { "$ref": "#/components/schemas/EmptyObjectResponse" }, { "$ref": "#/components/schemas/CsrfTokenResponse" }, { "$ref": "#/components/schemas/EmptyObjectResponse" } ] } } } }, "302": { "description": "Redirect after sign-in, OAuth callback, sign-out, or error handling. Session cookies may be set or cleared.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RedirectResponse" } } } } } } }, "/api/auth/session": { "get": { "summary": "Get current Auth.js session", "description": "Returns the current Auth.js/NextAuth session by reading the session cookie and running the full auth handler, including session revocation checks. Authenticated responses include the user profile, tenant context, session_id from the sessions table, and login method. If no valid session cookie is present, the route still returns HTTP 200 with an empty object.", "tags": [ "Auth" ], "security": [ { "SessionCookieAuth": [] } ], "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Authenticated session object, or {} when the request is unauthenticated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthSessionResponse" } } } }, "500": { "description": "Unexpected session retrieval failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FlatErrorResponse" } } } } } } }, "/api/auth/validate-api-key": { "post": { "summary": "Validate API key", "description": "Validates a plaintext API key supplied in the x-api-key header. The key is hashed before lookup. If the api_keys record exists, is active, has not expired, and has not exhausted its usage limit, the response returns the owning user UUID and tenant from that record. This route is the credential validation endpoint itself and does not require a separate session, tenant header, or RBAC permission.", "tags": [ "Auth" ], "security": [], "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "minLength": 1, "description": "Plaintext API key to validate. The service hashes this value before looking up the api_keys record." }, "required": true, "description": "Plaintext API key to validate. The service hashes this value before looking up the api_keys record.", "name": "x-api-key", "in": "header" } ], "responses": { "200": { "description": "API key is valid and active.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateApiKeyResponse" } } } }, "401": { "description": "The x-api-key header is missing, or the key is invalid, inactive, expired, or usage-exhausted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FlatErrorResponse" } } } }, "500": { "description": "Unexpected error while validating the API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FlatErrorResponse" } } } } } } }, "/api/auth/validate-token": { "post": { "summary": "Validate session token", "description": "Checks whether the request carries a valid Auth.js session token, either in the session cookie or in an Authorization: Bearer header. A valid token returns the user type and tenant copied from the session JWT. No request body is read.", "tags": [ "Auth" ], "security": [ { "SessionCookieAuth": [] }, { "BearerAuth": [] } ], "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Optional Bearer token fallback. The route also accepts the Auth.js session token cookie." }, "required": false, "description": "Optional Bearer token fallback. The route also accepts the Auth.js session token cookie.", "name": "authorization", "in": "header" } ], "responses": { "200": { "description": "Session token is valid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateTokenSuccessResponse" } } } }, "401": { "description": "No valid session token was found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateTokenUnauthorizedResponse" } } } }, "500": { "description": "Unexpected token validation error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FlatErrorResponse" } } } } } } }, "/api/v1/admin/telemetry-settings": { "get": { "summary": "Get telemetry settings", "description": "Returns tenant admin telemetry status derived from the ALGA_USAGE_STATS environment variable. The handler resolves the tenant with createTenantKnex, requires an authenticated current user, and checks the tenant-scoped users.role value for admin or owner. The route does not read or write tenant telemetry settings from the database. Because this session-authenticated route is not currently in apiKeySkipPaths, API middleware also requires an x-api-key header to be present before the handler can run.", "tags": [ "Admin", "Telemetry" ], "security": [ { "ApiKeyAuth": [], "SessionCookieAuth": [] } ], "extensions": { "x-rbac-roles": [ "admin", "owner" ], "x-tenant-scoped": true, "x-controlled-by": "ALGA_USAGE_STATS", "x-middleware-api-key-required-currently": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Current telemetry setting returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TelemetrySettingsGetResponse" } } } }, "401": { "description": "No authenticated current user, no tenant context, or x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminTelemetryErrorResponse" } } } }, "403": { "description": "Authenticated user is not an admin or owner in the tenant.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminTelemetryErrorResponse" } } } }, "500": { "description": "Unexpected failure while loading telemetry settings.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminTelemetryErrorResponse" } } } } } }, "post": { "summary": "Check telemetry settings update status", "description": "No-op endpoint for telemetry settings. The handler authenticates the current user, verifies the tenant-scoped users.role is admin or owner, ignores the request body, performs no database write, and returns the current ALGA_USAGE_STATS-derived status with a message explaining that telemetry cannot be changed through the API. Because this session-authenticated route is not currently in apiKeySkipPaths, API middleware also requires an x-api-key header to be present before the handler can run.", "tags": [ "Admin", "Telemetry" ], "security": [ { "ApiKeyAuth": [], "SessionCookieAuth": [] } ], "extensions": { "x-rbac-roles": [ "admin", "owner" ], "x-tenant-scoped": true, "x-controlled-by": "ALGA_USAGE_STATS", "x-request-body-ignored": true, "x-runtime-mutation": false, "x-middleware-api-key-required-currently": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Telemetry status returned; no setting was changed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TelemetrySettingsPostResponse" } } } }, "401": { "description": "No authenticated current user, no tenant context, or x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminTelemetryErrorResponse" } } } }, "403": { "description": "Authenticated user is not an admin or owner in the tenant.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminTelemetryErrorResponse" } } } }, "500": { "description": "Unexpected failure while processing the no-op update request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminTelemetryErrorResponse" } } } } } } }, "/api/v1/permission-checks": { "post": { "summary": "Check user permissions", "description": "Checks one or more resource/action pairs for the requested user (or current API-key user).", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "permission", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "checkUserPermissions" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessPermissionChecksBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for permission:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/permissions": { "get": { "summary": "List permissions", "description": "Lists permissions with optional resource/action filters.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "permission", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "listPermissions" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "resource", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "action", "in": "query" } ], "responses": { "200": { "description": "Paginated result set returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiPaginatedV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for permission:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "post": { "summary": "Create permission", "description": "Creates one tenant-scoped permission row.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "permission", "x-rbac-action": "create", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "createPermission" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessCreatePermissionBodyV1" } } } }, "responses": { "201": { "description": "Resource created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for permission:create.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/permissions/categories": { "get": { "summary": "List permission categories", "description": "Returns grouped permissions by resource.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "permission", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getPermissionCategories" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for permission:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/permissions/{id}": { "get": { "summary": "Get permission", "description": "Loads one permission by permission UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "permission", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getPermissionById" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Permission UUID from permissions.permission_id." }, "required": true, "description": "Permission UUID from permissions.permission_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for permission:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "put": { "summary": "Update permission", "description": "Updates permission metadata by permission UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "permission", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "updatePermission" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Permission UUID from permissions.permission_id." }, "required": true, "description": "Permission UUID from permissions.permission_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessUpdatePermissionBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for permission:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "delete": { "summary": "Delete permission", "description": "Deletes a permission by permission UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "permission", "x-rbac-action": "delete", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "deletePermission" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Permission UUID from permissions.permission_id." }, "required": true, "description": "Permission UUID from permissions.permission_id.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Operation completed with no response body." }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for permission:delete.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/permissions/{id}/roles": { "get": { "summary": "List roles using permission", "description": "Lists roles currently assigned the specified permission UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "permission", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getRolesByPermission" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Permission UUID from permissions.permission_id." }, "required": true, "description": "Permission UUID from permissions.permission_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for permission:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/rbac/analytics": { "get": { "summary": "Get RBAC analytics", "description": "Returns access-control metrics from PermissionRoleService.getAccessControlMetrics().", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "role", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getAccessControlMetrics" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for role:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/rbac/audit": { "get": { "summary": "Get RBAC audit log", "description": "Lists RBAC audit-log entries from audit_logs (roles, permissions, role_permissions, user_roles), newest first. Supports page/limit pagination and user_id, operation, table_name, record_id, start_date, and end_date filters. Gated on role:read.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "role", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "listRbacAudit" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for role:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/roles": { "get": { "summary": "List roles", "description": "Lists tenant roles with role-name and permission filters.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "role", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "listRoles" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "role_name", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_permissions", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "permission_resource", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "permission_action", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_template", "in": "query" } ], "responses": { "200": { "description": "Paginated result set returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiPaginatedV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for role:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "post": { "summary": "Create role", "description": "Creates one tenant role, optionally with initial permission assignments.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "role", "x-rbac-action": "create", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "createRole" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessCreateRoleBodyV1" } } } }, "responses": { "201": { "description": "Resource created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for role:create.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/roles/bulk": { "post": { "summary": "Bulk create roles", "description": "Processes an array of role payloads and returns per-item success/error entries.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "role", "x-rbac-action": "create", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "bulkCreateRoles" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessBulkRolesBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for role:create.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/roles/templates": { "get": { "summary": "List role templates", "description": "Returns predefined role templates for the tenant context.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "role", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getRoleTemplates" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for role:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/roles/{id}": { "get": { "summary": "Get role", "description": "Loads role by role UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "role", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getRoleById" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Role UUID from roles.role_id." }, "required": true, "description": "Role UUID from roles.role_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for role:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "put": { "summary": "Update role", "description": "Updates role metadata and optional permission references.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "role", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "updateRole" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Role UUID from roles.role_id." }, "required": true, "description": "Role UUID from roles.role_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessUpdateRoleBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for role:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "delete": { "summary": "Delete role", "description": "Deletes role by role UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "role", "x-rbac-action": "delete", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "deleteRole" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Role UUID from roles.role_id." }, "required": true, "description": "Role UUID from roles.role_id.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Operation completed with no response body." }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for role:delete.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/roles/{id}/clone": { "post": { "summary": "Clone role", "description": "Clones role configuration into a new role record.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "role", "x-rbac-action": "create", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "cloneRole" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Role UUID from roles.role_id." }, "required": true, "description": "Role UUID from roles.role_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessRoleCloneBodyV1" } } } }, "responses": { "201": { "description": "Resource created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for role:create.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/roles/{id}/permissions": { "get": { "summary": "Get role permissions", "description": "Returns permission set currently linked to the role UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "role", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getRolePermissions" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Role UUID from roles.role_id." }, "required": true, "description": "Role UUID from roles.role_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for role:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "put": { "summary": "Replace role permissions", "description": "Assigns provided permission UUIDs to the role and returns updated role payload.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "role", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "assignRolePermissions" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Role UUID from roles.role_id." }, "required": true, "description": "Role UUID from roles.role_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessRolePermissionsBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for role:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/teams": { "get": { "summary": "List teams", "description": "Lists teams through ApiBaseController list behavior.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "listTeams" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "team_name", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "manager_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" } ], "responses": { "200": { "description": "Paginated result set returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiPaginatedV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "post": { "summary": "Create team", "description": "Creates one team with optional manager and embedded members.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "create", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "createTeam" }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessCreateTeamBodyV1" } } } }, "responses": { "201": { "description": "Resource created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:create.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/teams/bulk": { "put": { "summary": "Bulk update teams", "description": "Updates many teams using {team_ids, updates} payload.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "bulkUpdateTeams" }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessTeamBulkUpdateBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "delete": { "summary": "Bulk delete teams", "description": "Deletes many teams using {team_ids} payload.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "delete", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "bulkDeleteTeams" }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessTeamBulkDeleteBodyV1" } } } }, "responses": { "204": { "description": "Operation completed with no response body." }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:delete.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/teams/hierarchy": { "get": { "summary": "Get team hierarchy", "description": "Current controller parses team id from URL tail; this route passes literal \"hierarchy\" as id and can fail validation/service lookup.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getTeamHierarchyRoot", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa", "algadesk" ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/teams/search": { "get": { "summary": "Search teams", "description": "Runs advanced team search filters and returns paginated data.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "searchTeams" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "manager_id", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_manager", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" } ], "responses": { "200": { "description": "Paginated result set returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiPaginatedV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/teams/stats": { "get": { "summary": "Get team stats", "description": "Returns aggregate team statistics for the tenant.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getTeamStats" }, "x-alga-products": [ "psa", "algadesk" ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/teams/{id}": { "get": { "summary": "Get team", "description": "Loads team by team UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getTeamById" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "put": { "summary": "Update team", "description": "Updates team metadata by team UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "updateTeam" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessUpdateTeamBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "delete": { "summary": "Delete team", "description": "Deletes a team by team UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "delete", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "deleteTeam" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Operation completed with no response body." }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:delete.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/teams/{id}/analytics": { "get": { "summary": "Get team analytics", "description": "Returns team analytics for the target team UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getTeamAnalytics" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "id", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "start_date", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "end_date", "in": "query" }, { "schema": { "type": "string", "description": "Controller query parser provides strings; schema expects metric array." }, "required": false, "description": "Controller query parser provides strings; schema expects metric array.", "name": "include_metrics", "in": "query" }, { "schema": { "type": "string", "enum": [ "daily", "weekly", "monthly" ] }, "required": false, "name": "granularity", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/teams/{id}/hierarchy": { "post": { "summary": "Attach team to parent hierarchy", "description": "Creates parent relationship for team UUID using parent_team_id payload.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "createTeamHierarchy" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessTeamHierarchyBodyV1" } } } }, "responses": { "201": { "description": "Resource created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "delete": { "summary": "Detach team from hierarchy", "description": "Removes hierarchy relationship for team UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "removeTeamHierarchy" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Operation completed with no response body." }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/teams/{id}/manager": { "put": { "summary": "Assign team manager", "description": "Assigns manager_id to team UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "assignTeamManager" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessAssignTeamManagerBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/teams/{id}/members": { "get": { "summary": "List team members", "description": "Returns members for team UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "listTeamMembers" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "post": { "summary": "Add team member", "description": "Adds one user to team UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "addTeamMember" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessAddTeamMemberBodyV1" } } } }, "responses": { "201": { "description": "Resource created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/teams/{id}/members/bulk": { "post": { "summary": "Bulk add team members", "description": "Adds many users to team UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "bulkAddTeamMembers" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessBulkAddTeamMembersBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/teams/{id}/members/{userId}": { "delete": { "summary": "Remove team member", "description": "Removes one user from team UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "removeTeamMember" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "userId", "in": "path" } ], "responses": { "204": { "description": "Operation completed with no response body." }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/teams/{id}/permissions": { "get": { "summary": "List team permissions", "description": "Lists ACL records attached to team UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "listTeamPermissions" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "post": { "summary": "Grant team permission", "description": "Creates one permission grant for team UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "grantTeamPermission" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessTeamPermissionGrantBodyV1" } } } }, "responses": { "201": { "description": "Resource created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/teams/{id}/permissions/{permissionId}": { "delete": { "summary": "Revoke team permission", "description": "Revokes permission grant. Controller extracts permission id from path and does not use team id during revocation call.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "revokeTeamPermission" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Permission UUID from permissions.permission_id." }, "required": true, "description": "Permission UUID from permissions.permission_id.", "name": "permissionId", "in": "path" } ], "responses": { "204": { "description": "Operation completed with no response body." }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/teams/{id}/projects": { "get": { "summary": "List team projects", "description": "Lists projects associated with team UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "team", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "listTeamProjects" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for team:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/user-roles": { "get": { "summary": "List users with roles", "description": "Returns paginated users plus their role assignments.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "listUsersWithRoles" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_inactive", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" } ], "responses": { "200": { "description": "Paginated result set returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiPaginatedV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users": { "get": { "summary": "List users", "description": "Lists users using base list behavior and user query filters.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "listUsers" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "username", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "first_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "last_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "email", "in": "query" }, { "schema": { "type": "string", "enum": [ "internal", "client", "admin", "contractor" ] }, "required": false, "name": "user_type", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "role_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "team_id", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_inactive", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_permissions", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_teams", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" } ], "responses": { "200": { "description": "Paginated result set returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiPaginatedV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "post": { "summary": "Create user", "description": "Creates one user using createUserSchema validation.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "create", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "createUser" }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessCreateUserBodyV1" } } } }, "responses": { "201": { "description": "Resource created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:create.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/activity": { "get": { "summary": "List global user activity", "description": "Returns paginated activity feed across users.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "listUserActivity" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "from_date", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "to_date", "in": "query" }, { "schema": { "type": "string", "description": "Controller converts single query value to one-element array for service filter." }, "required": false, "description": "Controller converts single query value to one-element array for service filter.", "name": "activity_type", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "ip_address", "in": "query" } ], "responses": { "200": { "description": "Paginated result set returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiPaginatedV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/bulk/create": { "post": { "summary": "Bulk create users (route currently mapped to single create)", "description": "Route currently delegates to ApiUserController.create(), so behavior is single-user create schema rather than bulk payload handling.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "create", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "bulkCreateUsersRouteMismatch", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessCreateUserBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:create.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/bulk/deactivate": { "put": { "summary": "Bulk deactivate users (route currently mapped to update-by-id)", "description": "Route currently delegates to ApiUserController.update(); id extraction reads \"bulk\" from path and fails UUID validation.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "bulkDeactivateUsersRouteMismatch", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessUpdateUserBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Current route fails UUID extraction because ApiBaseController.update() treats path segment \"bulk\" as {id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/search": { "get": { "summary": "Search users", "description": "Searches users with optional NM-store system context support and query validation.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "searchUsers" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": true, "name": "query", "in": "query" }, { "schema": { "type": "string", "description": "Controller query parser sends strings; schema expects an array of field names." }, "required": false, "description": "Controller query parser sends strings; schema expects an array of field names.", "name": "fields", "in": "query" }, { "schema": { "type": "string", "enum": [ "internal", "client", "admin", "contractor" ] }, "required": false, "name": "user_type", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "role_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "team_id", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_inactive", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" } ], "responses": { "200": { "description": "Paginated result set returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiPaginatedV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/stats": { "get": { "summary": "Get user stats", "description": "Returns aggregate user statistics.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getUserStats" }, "x-alga-products": [ "psa", "algadesk" ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/{id}": { "get": { "summary": "Get user", "description": "Loads user by user UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getUserById" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "put": { "summary": "Update user", "description": "Updates user by user UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "updateUser" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessUpdateUserBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "delete": { "summary": "Delete user", "description": "Deletes user by user UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "delete", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "deleteUser" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:delete.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/{id}/2fa/disable": { "delete": { "summary": "Disable user 2FA", "description": "Disables two-factor authentication for target user UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "disableUser2FA" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/{id}/2fa/enable": { "post": { "summary": "Enable user 2FA", "description": "Enables two-factor authentication using secret/token payload.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "enableUser2FA" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessEnable2FABodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/{id}/activity": { "get": { "summary": "Get user activity", "description": "Returns activity log for one user UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getUserActivityById" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "from_date", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "to_date", "in": "query" }, { "schema": { "type": "string", "description": "Controller converts single query value to one-element array for service filter." }, "required": false, "description": "Controller converts single query value to one-element array for service filter.", "name": "activity_type", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "ip_address", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/{id}/avatar": { "post": { "summary": "Upload user avatar", "description": "Uploads avatar file from multipart form field avatar.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "uploadUserAvatar" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "avatar": { "type": "string", "description": "Multipart file field name expected by controller: avatar." } }, "required": [ "avatar" ] } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "delete": { "summary": "Delete user avatar", "description": "Deletes avatar for target user UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "deleteUserAvatar" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/{id}/password": { "put": { "summary": "Change user password", "description": "Changes password for self or another user with user:update permission.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "changeUserPassword" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessChangePasswordBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/{id}/permissions": { "get": { "summary": "Get user effective permissions", "description": "Returns explicit/effective permissions and roles for user UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getUserPermissions" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/{id}/preferences": { "get": { "summary": "Get user preferences", "description": "Returns user preference map for user UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getUserPreferences" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "put": { "summary": "Update user preferences", "description": "Updates user preference payload without route-level schema validation.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "updateUserPreferences" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessUserPreferenceBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/{id}/roles": { "get": { "summary": "List user roles", "description": "Lists roles assigned to user UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getUserRoles" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "post": { "summary": "Assign user roles", "description": "Assigns provided role_ids to user UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "assignUserRoles" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessUserRoleIdsBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "delete": { "summary": "Remove user roles", "description": "Removes provided role_ids from user UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "removeUserRoles" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessUserRoleIdsBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "put": { "summary": "Replace user roles", "description": "Replaces all role links for user UUID using transaction over user_roles.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "update", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "replaceUserRoles" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessUserRoleIdsBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/{id}/teams": { "get": { "summary": "List user teams", "description": "Returns teams associated with user UUID.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "read", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "getUserTeams" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } }, "post": { "summary": "Add user to team (route currently mapped to user create)", "description": "Route currently delegates to ApiUserController.create(); payload is interpreted as create-user body rather than team membership.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "create", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "userTeamsCreateRouteMismatch", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessCreateUserBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiSuccessV1" } } } }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:create.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/users/{id}/teams/{teamId}": { "delete": { "summary": "Remove user from team (route currently mapped to user delete)", "description": "Route currently delegates to ApiUserController.delete(); extracted id is user UUID and teamId path segment is ignored.", "tags": [ "Access Control & Users v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "user", "x-rbac-action": "delete", "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or equivalent controller-specific auth path", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-controller-handler": "userTeamsDeleteRouteMismatch", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "User UUID from users.user_id." }, "required": true, "description": "User UUID from users.user_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Team UUID from teams.team_id." }, "required": true, "description": "Team UUID from teams.team_id.", "name": "teamId", "in": "path" } ], "responses": { "204": { "description": "Current handler delegates to ApiUserController.delete(); this removes the user record, not just team membership." }, "400": { "description": "Validation failed (payload/query/path parsing).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "403": { "description": "RBAC denied for user:delete.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "404": { "description": "User not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessApiErrorV1" } } } } } } }, "/api/v1/assets": { "get": { "summary": "List assets", "description": "Lists assets for the authenticated tenant with pagination, sorting, and asset filters. The controller validates query parameters with assetListQuerySchema, queries assets filtered by assets.tenant, joins clients for client_name, computes warranty_status, and adds HATEOAS links from asset_id. The route is authenticated and tenant-scoped via withApiKeyRouteAuth (req.context is populated before the handler runs).", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "read", "x-unapplied-validated-filters": [ "maintenance_due", "is_active", "search", "created_from", "created_to", "updated_from", "updated_to" ] }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Page number as a query string. Defaults to 1 after validation." }, "required": false, "description": "Page number as a query string. Defaults to 1 after validation.", "name": "page", "in": "query" }, { "schema": { "type": "string", "description": "Page size as a query string. Must parse to 1 through 100; defaults to 25." }, "required": false, "description": "Page size as a query string. Must parse to 1 through 100; defaults to 25.", "name": "limit", "in": "query" }, { "schema": { "type": "string", "description": "Sort column. Defaults to created_at." }, "required": false, "description": "Sort column. Defaults to created_at.", "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ], "description": "Sort direction. Defaults to desc." }, "required": false, "description": "Sort direction. Defaults to desc.", "name": "order", "in": "query" }, { "schema": { "type": "string", "description": "General search filter accepted by the shared filter schema. The current AssetService.list implementation does not apply this filter." }, "required": false, "description": "General search filter accepted by the shared filter schema. The current AssetService.list implementation does not apply this filter.", "name": "search", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by validation, but not currently applied by AssetService.list." }, "required": false, "description": "Accepted by validation, but not currently applied by AssetService.list.", "name": "created_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by validation, but not currently applied by AssetService.list." }, "required": false, "description": "Accepted by validation, but not currently applied by AssetService.list.", "name": "created_to", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by validation, but not currently applied by AssetService.list." }, "required": false, "description": "Accepted by validation, but not currently applied by AssetService.list.", "name": "updated_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by validation, but not currently applied by AssetService.list." }, "required": false, "description": "Accepted by validation, but not currently applied by AssetService.list.", "name": "updated_to", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ], "description": "Accepted by validation, but not currently applied by AssetService.list." }, "required": false, "description": "Accepted by validation, but not currently applied by AssetService.list.", "name": "is_active", "in": "query" }, { "schema": { "type": "string", "description": "Partial asset tag match using ILIKE." }, "required": false, "description": "Partial asset tag match using ILIKE.", "name": "asset_tag", "in": "query" }, { "schema": { "type": "string", "description": "Partial asset name match using ILIKE." }, "required": false, "description": "Partial asset name match using ILIKE.", "name": "name", "in": "query" }, { "schema": { "type": "string", "format": "uuid", "description": "Client UUID from clients.client_id." }, "required": false, "description": "Client UUID from clients.client_id.", "name": "client_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid", "description": "Client location UUID from client_locations.location_id." }, "required": false, "description": "Client location UUID from client_locations.location_id.", "name": "location_id", "in": "query" }, { "schema": { "type": "string", "enum": [ "workstation", "network_device", "server", "mobile_device", "printer", "unknown" ], "description": "Asset type stored in assets.asset_type." }, "required": false, "description": "Asset type stored in assets.asset_type.", "name": "asset_type", "in": "query" }, { "schema": { "type": "string", "description": "Exact asset status match." }, "required": false, "description": "Exact asset status match.", "name": "status", "in": "query" }, { "schema": { "type": "string", "description": "Partial location match using ILIKE." }, "required": false, "description": "Partial location match using ILIKE.", "name": "location", "in": "query" }, { "schema": { "type": "string", "description": "Partial client name match; joins clients on client_id and tenant." }, "required": false, "description": "Partial client name match; joins clients on client_id and tenant.", "name": "client_name", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ], "description": "true requires warranty_end_date to be non-null; false requires it to be null." }, "required": false, "description": "true requires warranty_end_date to be non-null; false requires it to be null.", "name": "has_warranty", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ], "description": "true filters warranty_end_date before now; false filters future warranty dates or no warranty." }, "required": false, "description": "true filters warranty_end_date before now; false filters future warranty dates or no warranty.", "name": "warranty_expired", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ], "description": "Accepted by validation, but not currently applied by AssetService.list." }, "required": false, "description": "Accepted by validation, but not currently applied by AssetService.list.", "name": "maintenance_due", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Filter purchase_date greater than or equal to this timestamp." }, "required": false, "description": "Filter purchase_date greater than or equal to this timestamp.", "name": "purchase_date_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Filter purchase_date less than or equal to this timestamp." }, "required": false, "description": "Filter purchase_date less than or equal to this timestamp.", "name": "purchase_date_to", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Filter warranty_end_date greater than or equal to this timestamp." }, "required": false, "description": "Filter warranty_end_date greater than or equal to this timestamp.", "name": "warranty_end_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Filter warranty_end_date less than or equal to this timestamp." }, "required": false, "description": "Filter warranty_end_date less than or equal to this timestamp.", "name": "warranty_end_to", "in": "query" } ], "responses": { "200": { "description": "Paginated asset list returned successfully. The pagination and links are nested under the top-level data envelope.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetListResponse" } } } }, "400": { "description": "Query parameter validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to read assets when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } }, "post": { "summary": "Create asset", "description": "Creates an asset for the authenticated tenant. The request body is validated with createAssetWithExtensionSchema; client_id, asset_type, asset_tag, name, and status are required. AssetService.create writes assets.tenant from the request context, inserts the asset, optionally upserts asset-type-specific extension_data, publishes an ASSET_CREATED event, and returns getWithDetails with HATEOAS links.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "create", "x-publishes-event": "ASSET_CREATED" }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Asset fields plus optional asset-type-specific extension_data.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetCreateRequest" }, "description": "Asset fields plus optional asset-type-specific extension_data." } } }, "responses": { "201": { "description": "Asset created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetResourceResponse" } } } }, "400": { "description": "Request body validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to create assets when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/bulk-status": { "put": { "summary": "Bulk update asset status", "description": "Updates the status field for up to 50 assets in the authenticated tenant. The controller validates asset_ids and status with bulkAssetStatusSchema, then calls AssetService.update for each asset_id with { status }. Each update is tenant-scoped by assets.asset_id and assets.tenant and publishes an ASSET_UPDATED event.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "update", "x-max-items": 50, "x-publishes-event": "ASSET_UPDATED" }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Asset IDs and the new status to apply to all assets.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetBulkStatusRequest" }, "description": "Asset IDs and the new status to apply to all assets." } } }, "responses": { "200": { "description": "Status updated for all requested assets.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetBulkUpdateResponse" } } } }, "400": { "description": "Request body validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to update assets when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/bulk-update": { "put": { "summary": "Bulk update assets", "description": "Updates up to 50 assets in the authenticated tenant. Each array item supplies an asset_id and partial update data validated with updateAssetSchema. The controller calls AssetService.update for every item, tenant-scoping each update by asset_id and context.tenant and publishing ASSET_UPDATED events.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "update", "x-max-items": 50, "x-publishes-event": "ASSET_UPDATED" }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Array of asset_id plus partial update data objects.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetBulkUpdateRequest" }, "description": "Array of asset_id plus partial update data objects." } } }, "responses": { "200": { "description": "Assets updated successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetBulkUpdateResponse" } } } }, "400": { "description": "Request body validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to update assets when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/documents/{associationId}": { "delete": { "summary": "Remove asset document association", "description": "Removes a document association row by document_associations.association_id for the authenticated tenant. The service deletes rows where association_id and tenant match and does not verify entity_type in this method. The controller intends to return an empty success response after deletion, but currently calls createApiResponse(null, 204) inside NextResponse.json, which can throw because JSON responses cannot use status 204 with a body.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "update", "x-current-204-json-response-bug": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Document association UUID from document_associations.association_id." }, "required": true, "description": "Document association UUID from document_associations.association_id.", "name": "associationId", "in": "path" } ], "responses": { "204": { "description": "Intended successful deletion response with no body." }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to update asset documents when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error, including the current 204 JSON response construction issue.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/export": { "get": { "summary": "Export assets", "description": "Exports all assets for the authenticated tenant (paginated internally, not capped to the default list page). Optional set filters asset_types, statuses, and client_ids narrow the result; fields restricts the exported columns. format=csv (default) returns text/csv with Content-Disposition attachment filename=assets.csv; format=json returns the success envelope with the rows in data. The route is authenticated and tenant-scoped via withApiKeyRouteAuth.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": false, "x-rbac-resource": "asset", "x-rbac-action": "read", "x-csv-content-disposition": "attachment; filename=assets.csv" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "enum": [ "csv", "json" ], "description": "Export format. csv (default) returns text/csv as an attachment; json returns the success envelope with the rows in data." }, "required": false, "description": "Export format. csv (default) returns text/csv as an attachment; json returns the success envelope with the rows in data.", "name": "format", "in": "query" }, { "schema": { "type": "array", "items": { "type": "string", "enum": [ "workstation", "network_device", "server", "mobile_device", "printer", "unknown" ] }, "description": "Filter to these asset types. Repeat the param or pass a comma-separated list." }, "required": false, "description": "Filter to these asset types. Repeat the param or pass a comma-separated list.", "name": "asset_types", "in": "query" }, { "schema": { "type": "array", "items": { "type": "string" }, "description": "Filter to these asset statuses. Repeat the param or pass a comma-separated list." }, "required": false, "description": "Filter to these asset statuses. Repeat the param or pass a comma-separated list.", "name": "statuses", "in": "query" }, { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Filter to these client UUIDs. Repeat the param or pass a comma-separated list." }, "required": false, "description": "Filter to these client UUIDs. Repeat the param or pass a comma-separated list.", "name": "client_ids", "in": "query" }, { "schema": { "type": "array", "items": { "type": "string" }, "description": "Restrict exported columns to this set. Repeat the param or pass a comma-separated list." }, "required": false, "description": "Restrict exported columns to this set. Repeat the param or pass a comma-separated list.", "name": "fields", "in": "query" } ], "responses": { "200": { "description": "Asset export returned successfully. For format=csv (or omitted) the handler returns text/csv with an attachment filename; for format=json it returns this JSON envelope.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetExportJsonResponse" } } } }, "400": { "description": "Query parameter validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "401": { "description": "x-api-key is missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks asset:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/maintenance/{scheduleId}": { "delete": { "summary": "Delete asset maintenance schedule", "description": "Deletes a maintenance schedule by asset_maintenance_schedules.schedule_id for the authenticated tenant. The service scopes deletion by schedule_id and context.tenant and performs no existence check, so missing or cross-tenant IDs are silent no-ops. The controller intends to return 204 with no body, but currently constructs a JSON response with status 204, which can throw in NextResponse.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "delete", "x-idempotent": true, "x-no-existence-check": true, "x-current-204-json-response-bug": true, "x-no-event-published": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Maintenance schedule UUID from asset_maintenance_schedules.schedule_id." }, "required": true, "description": "Maintenance schedule UUID from asset_maintenance_schedules.schedule_id.", "name": "scheduleId", "in": "path" } ], "responses": { "204": { "description": "Maintenance schedule deleted successfully, or it was already absent." }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to delete or update asset maintenance schedules when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error, including the current 204 JSON response construction issue.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } }, "put": { "summary": "Update asset maintenance schedule", "description": "Updates a maintenance schedule by asset_maintenance_schedules.schedule_id for the authenticated tenant. All request fields are optional because updateMaintenanceScheduleSchema is a partial form of the create schema. If frequency, frequency_interval, or start_date are supplied, the service loads the existing schedule for the tenant and recalculates next_maintenance. Missing or cross-tenant schedule IDs result in a 200 response with an undefined nested data value rather than 404.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "update", "x-recalculates-next-maintenance": true, "x-no-not-found-on-missing-id": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Maintenance schedule UUID from asset_maintenance_schedules.schedule_id." }, "required": true, "description": "Maintenance schedule UUID from asset_maintenance_schedules.schedule_id.", "name": "scheduleId", "in": "path" } ], "requestBody": { "description": "Partial maintenance schedule update data.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMaintenanceScheduleUpdateRequest" }, "description": "Partial maintenance schedule update data." } } }, "responses": { "200": { "description": "Maintenance schedule updated successfully. If the ID is not found, the nested data field may be absent.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMaintenanceScheduleResponse" } } } }, "400": { "description": "Request body validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to update asset maintenance schedules when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/relationships/{relationshipId}": { "delete": { "summary": "Delete asset relationship", "description": "Deletes an asset relationship by asset_relationships.relationship_id for the authenticated tenant. The service hard-deletes rows where relationship_id and context.tenant match, publishes no event, and performs no existence check; missing or cross-tenant IDs are silent no-ops. The controller intends to return 204 with no body, but currently constructs a JSON response with status 204, which can throw in NextResponse.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "update", "x-idempotent": true, "x-no-existence-check": true, "x-current-204-json-response-bug": true, "x-no-event-published": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset relationship UUID from asset_relationships.relationship_id." }, "required": true, "description": "Asset relationship UUID from asset_relationships.relationship_id.", "name": "relationshipId", "in": "path" } ], "responses": { "204": { "description": "Relationship deleted successfully, or it was already absent." }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to update asset relationships when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error, including the current 204 JSON response construction issue.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/search": { "get": { "summary": "Search assets", "description": "Searches tenant assets with a required query term and optional field, asset type, status, client, extension-data, and limit parameters. The service searches assets scoped to context.tenant, joins clients for client_name, and optionally loads asset-type-specific extension data per result. The response is not paginated and includes HATEOAS links for each asset; the top-level search link currently points at /api/v2/assets/search.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "read", "x-not-paginated": true, "x-response-links-path-version": "v2" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "minLength": 1, "description": "Required search term used in ILIKE clauses." }, "required": true, "description": "Required search term used in ILIKE clauses.", "name": "query", "in": "query" }, { "schema": { "type": "array", "items": { "type": "string", "enum": [ "asset_tag", "name", "serial_number", "location", "client_name" ] }, "description": "Search fields. If omitted, the service searches asset_tag, name, serial_number, and location." }, "required": false, "description": "Search fields. If omitted, the service searches asset_tag, name, serial_number, and location.", "name": "fields", "in": "query" }, { "schema": { "type": "array", "items": { "type": "string", "enum": [ "workstation", "network_device", "server", "mobile_device", "printer", "unknown" ] }, "description": "Optional asset type filters." }, "required": false, "description": "Optional asset type filters.", "name": "asset_types", "in": "query" }, { "schema": { "type": "array", "items": { "type": "string" }, "description": "Optional status filters." }, "required": false, "description": "Optional status filters.", "name": "statuses", "in": "query" }, { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Optional client UUID filters." }, "required": false, "description": "Optional client UUID filters.", "name": "client_ids", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ], "description": "When true, the service fetches asset-type-specific extension data for every result." }, "required": false, "description": "When true, the service fetches asset-type-specific extension data for every result.", "name": "include_extension_data", "in": "query" }, { "schema": { "type": "string", "description": "Maximum result count as a query string. Must parse to 1 through 100; defaults to 25." }, "required": false, "description": "Maximum result count as a query string. Must parse to 1 through 100; defaults to 25.", "name": "limit", "in": "query" } ], "responses": { "200": { "description": "Matching assets returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetSearchResponse" } } } }, "400": { "description": "Query parameter validation failed, including missing required query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to read assets when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/stats": { "get": { "summary": "Get asset statistics", "description": "Returns tenant-scoped aggregate asset statistics including total counts, counts by type/status/client, warranty counts, and maintenance due/overdue counts. The service runs multiple aggregate queries filtered by context.tenant; assets_by_client is limited to the top 10 client names. The response links currently point at /api/v2/assets paths.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "read", "x-assets-by-client-limit": 10, "x-response-links-path-version": "v2" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Asset statistics returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetStatsResponse" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to read assets when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}": { "get": { "summary": "Get asset details", "description": "Returns detailed asset information for the authenticated tenant, including joined client_name, computed warranty_status, client details, type-specific extension_data, relationships, document associations, maintenance schedules, and HATEOAS links. The service first loads assets by asset_id and context.tenant, then loads related data in parallel. If the asset is not found, the controller returns a 404 error envelope.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "read", "x-includes-related-resources": [ "client", "extension_data", "relationships", "documents", "maintenance_schedules" ] }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Asset details returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetResourceResponse" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to read assets when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "No asset exists for the supplied asset_id in the authenticated tenant.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } }, "put": { "summary": "Update asset", "description": "Partially updates base asset fields for the authenticated tenant. The request body is validated with updateAssetSchema, where all fields are optional. AssetService.update scopes the update by asset_id and context.tenant, writes updated_at, publishes ASSET_UPDATED, and returns the refreshed base asset with joined client_name and warranty_status. This REST path does not update extension data, create asset history records, or wrap the update in a transaction. Missing assets currently lead to a 500 when the controller tries to add links to a null result rather than a clean 404.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "update", "x-publishes-event": "ASSET_UPDATED", "x-extension-data-handled": false, "x-history-recorded": false, "x-transactional": false, "x-no-clean-not-found-currently": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "requestBody": { "description": "Partial base asset update data. Extension data is not accepted by this REST endpoint.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetUpdateData" }, "description": "Partial base asset update data. Extension data is not accepted by this REST endpoint." } } }, "responses": { "200": { "description": "Asset updated successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetResourceResponse" } } } }, "400": { "description": "Request body validation failed, or the database rejected an invalid reference.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to update assets when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Intended not-found response for missing assets; the current controller path may surface this as 500.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "409": { "description": "Database unique constraint conflict, such as a duplicate asset tag if enforced by schema.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error, including null asset result handling.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } }, "delete": { "summary": "Delete asset", "description": "Hard-deletes an asset for the authenticated tenant. AssetService.delete first loads the asset by asset_id and context.tenant, deletes asset-type-specific extension data, deletes tenant_external_entity_mappings for the asset, deletes the assets row, and publishes ASSET_DELETED. The method overrides the BaseService softDelete configuration and does not explicitly clean every related table handled by the server-action delete path. Missing assets throw a generic Error and currently surface as 500 via handleApiError rather than 404. The controller also constructs a JSON response with status 204, which can throw in NextResponse.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "delete", "x-hard-delete": true, "x-publishes-event": "ASSET_DELETED", "x-current-204-json-response-bug": true, "x-no-clean-not-found-currently": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Asset deleted successfully. Intended response has no body." }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to delete assets when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Intended not-found response for missing assets; the current service throws a generic Error that may surface as 500.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error, including generic Asset not found errors or the current 204 JSON response construction issue.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}/documents": { "get": { "summary": "List asset document associations", "description": "Returns document_associations rows for an asset in the authenticated tenant where entity_type is asset and entity_id is the path asset ID. The service joins documents to add original_filename, file_size, mime_type, and uploaded_at. It does not check whether the asset exists first, so nonexistent or cross-tenant asset IDs return 200 with an empty array. Response links currently point at /api/v2/assets paths.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "read", "x-no-asset-existence-check": true, "x-response-links-path-version": "v2" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Document associations returned successfully. Empty when no associations exist or the asset is not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetDocumentListResponse" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to read asset documents when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } }, "post": { "summary": "Associate document with asset", "description": "Creates a document_associations row that links the path asset ID to the supplied document_id. The body is validated with createAssetDocumentSchema; document_id is required and notes is optional. The service inserts entity_type=asset, entity_id from the path, document_id, notes, tenant from context, and created_at, then returns the inserted row. It does not verify that the asset or document belongs to the same tenant before insert, does not set created_by, and publishes no event. Foreign-key or unique constraint errors surface through handleApiError.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "update", "x-no-asset-existence-check": true, "x-no-tenant-cross-check-for-document": true, "x-created-by-set": false, "x-no-event-published": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "requestBody": { "description": "Document UUID and optional association notes.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetDocumentAssociationRequest" }, "description": "Document UUID and optional association notes." } } }, "responses": { "201": { "description": "Document association created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetDocumentAssociationResponse" } } } }, "400": { "description": "Request body validation failed or a database foreign-key reference is invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to update asset documents when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "409": { "description": "Duplicate association rejected by a database unique constraint.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}/history": { "get": { "summary": "List asset maintenance history", "description": "Returns all maintenance history rows for the path asset ID in the authenticated tenant, ordered by performed_at descending. The service joins users to add performed_by_user_name and filters asset_maintenance_history by asset_id and context.tenant. It performs no asset existence check, so nonexistent or cross-tenant asset IDs return 200 with an empty array. Response links currently point at /api/v2/assets paths.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "read", "x-no-asset-existence-check": true, "x-not-paginated": true, "x-response-links-path-version": "v2" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Maintenance history rows returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMaintenanceHistoryResponse" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to read asset history when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}/maintenance": { "get": { "summary": "List asset maintenance schedules", "description": "Returns maintenance schedule rows for the path asset ID in the authenticated tenant. The service filters asset_maintenance_schedules by asset_id and context.tenant and joins users to add assigned_user_name. It performs no asset existence check, so nonexistent or cross-tenant asset IDs return 200 with an empty array. Response links currently point at /api/v2/assets paths.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "read", "x-no-asset-existence-check": true, "x-response-links-path-version": "v2" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Maintenance schedules returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMaintenanceScheduleListResponse" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to read asset maintenance schedules when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } }, "post": { "summary": "Create asset maintenance schedule", "description": "Creates a maintenance schedule for the path asset ID in the authenticated tenant. The request body is validated with createMaintenanceScheduleSchema; schedule_type and frequency are required, while start_date is optional in the current shared date schema and the service falls back to now when absent. The service inserts asset_id from the path, tenant from context, timestamps, and a calculated next_maintenance value. It does not verify asset existence, does not set created_by, and publishes no event.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "update", "x-no-asset-existence-check": true, "x-calculates-next-maintenance": true, "x-created-by-set": false, "x-no-event-published": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "requestBody": { "description": "Maintenance schedule creation data.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMaintenanceScheduleCreateRequest" }, "description": "Maintenance schedule creation data." } } }, "responses": { "201": { "description": "Maintenance schedule created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMaintenanceScheduleResponse" } } } }, "400": { "description": "Request body validation failed or the database rejected an invalid reference.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to create or update asset maintenance schedules when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "409": { "description": "Database unique constraint conflict if one is enforced.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}/maintenance/record": { "post": { "summary": "Record asset maintenance", "description": "Inserts an asset_maintenance_history row for the path asset ID in the authenticated tenant. The request body requires maintenance_type, performed_by, and performed_at; schedule_id, duration, cost, notes, parts, and structured maintenance_data are optional. If schedule_id is supplied and a matching tenant schedule exists, the service updates that schedule last_maintenance and next_maintenance. It does not verify asset existence or performed_by before insert, does not set created_by, and publishes no event.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "update", "x-no-asset-existence-check": true, "x-performed-by-not-validated": true, "x-schedule-update-on-linked": true, "x-created-by-set": false, "x-no-event-published": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "requestBody": { "description": "Maintenance history record data.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetRecordMaintenanceRequest" }, "description": "Maintenance history record data." } } }, "responses": { "201": { "description": "Maintenance record created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetRecordMaintenanceResponse" } } } }, "400": { "description": "Request body validation failed or the database rejected an invalid reference.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to update asset maintenance records when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "409": { "description": "Database unique constraint conflict if one is enforced.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}/relationships": { "get": { "summary": "List asset relationships", "description": "Returns asset_relationships rows for the path asset ID in the authenticated tenant, joined with the related assets table for display fields. The service filters by asset_relationships.asset_id and context.tenant and joins related assets on matching tenant. It performs no parent asset existence check and does not filter soft-deleted related assets. Response links currently point at /api/v2/assets paths.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "read", "x-no-asset-existence-check": true, "x-related-asset-soft-delete-filter": false, "x-response-links-path-version": "v2" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Asset relationships returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetRelationshipListResponse" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to read asset relationships when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } }, "post": { "summary": "Create asset relationship", "description": "Creates a relationship row between the path asset ID and related_asset_id for the authenticated tenant. The request body requires related_asset_id and non-empty relationship_type. The service rejects self relationships and duplicate source/related pairs, but currently throws generic Errors for those cases, which surface as 500 rather than 400 or 409. The inserted row is returned without joined related asset details.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": true, "x-rbac-resource": "asset", "x-rbac-action": "update", "x-self-relationship-check": true, "x-duplicate-check": true, "x-self-relationship-error-is-500": true, "x-duplicate-error-is-500": true, "x-no-event-published": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "requestBody": { "description": "Relationship creation data.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetRelationshipCreateRequest" }, "description": "Relationship creation data." } } }, "responses": { "201": { "description": "Asset relationship created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetRelationshipResponse" } } } }, "400": { "description": "Request body validation failed or a database reference is invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "401": { "description": "x-api-key is missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Authenticated request context lacks permission to update asset relationships when auth wiring is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "409": { "description": "Intended duplicate conflict response; current duplicate check throws a generic Error that may surface as 500.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error, including self-relationship or duplicate-relationship errors in the current implementation.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}/tickets": { "get": { "summary": "List tickets linked to an asset", "description": "Returns tickets associated with the path asset ID in the authenticated tenant, read from asset_associations where entity_type is ticket, joined to tickets and statuses. Results include the association relationship_type and linked_at and are ordered by ticket updated_at descending. This mirrors the asset detail UI and the assets.find_associated_tickets workflow action. The route is authenticated and tenant-scoped via withApiKeyRouteAuth (req.context is populated and RLS is active) and enforces BOTH asset:read and ticket:read because the response joins ticket data. It performs no asset existence check, so a valid caller hitting a nonexistent or cross-tenant asset ID gets 200 with an empty array (tenant scoping prevents cross-tenant leakage; this is not an authorization bypass).", "operationId": "listAssetTickets", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": false, "x-rbac-resource": "asset", "x-rbac-action": "read", "x-rbac-additional-required": "ticket:read", "x-no-asset-existence-check": true, "x-not-paginated": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Linked tickets returned successfully. Empty when no associations exist or the asset is not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetTicketListResponse" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks asset:read or ticket:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } }, "post": { "summary": "Link a ticket to an asset", "description": "Creates an asset_associations row (entity_type=ticket) linking the path asset to the ticket in the request body. This is the same table the asset detail UI, GET /api/v1/assets/{id}/tickets, and GET /api/v1/tickets/{id}/assets read, so the link is immediately visible from both sides. relationship_type defaults to affected. The route is authenticated and tenant-scoped via withApiKeyRouteAuth and enforces asset:update (the asset associations are being mutated) plus ticket:read (the ticket is only referenced). Both the asset and the ticket must exist in the tenant (404 otherwise), and a duplicate link returns 409.", "operationId": "linkTicketToAsset", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": false, "x-rbac-resource": "asset", "x-rbac-action": "update", "x-rbac-additional-required": "ticket:read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "requestBody": { "description": "The ticket to link plus optional relationship_type and notes.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetTicketLinkRequest" }, "description": "The ticket to link plus optional relationship_type and notes." } } }, "responses": { "201": { "description": "Ticket linked. Returns the inserted asset_associations row.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetTicketLinkResponse" } } } }, "400": { "description": "Request body validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks asset:update or ticket:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Asset or ticket not found in the tenant.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "409": { "description": "Ticket is already linked to this asset.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/tickets/{id}/assets": { "get": { "summary": "List assets linked to a ticket", "description": "Returns assets associated with the path ticket ID in the authenticated tenant, read from asset_associations where entity_type is ticket, joined to assets and clients. Each row includes the asset fields plus the association relationship_type, association_notes, and linked_at, ordered by link time descending. The route is authenticated and tenant-scoped via ApiBaseController.authenticate + runWithTenant (RLS active); it enforces ticket:read, a per-ticket authorization-kernel check (assertTicketReadAllowed), AND asset:read because the response exposes asset records. The response uses the apiMiddleware envelope ({ data } with no top-level success field). It performs no asset existence check beyond the ticket authorization, so an authorized caller on a ticket with no linked assets gets 200 with an empty array.", "operationId": "listTicketAssets", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-rbac-resource": "ticket", "x-rbac-action": "read", "x-rbac-additional-required": "asset:read", "x-per-record-authorization": "assertTicketReadAllowed", "x-not-paginated": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Linked assets returned successfully. Empty when the ticket has no linked assets.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TicketAssetListResponse" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks ticket:read (or per-ticket authorization) or asset:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Ticket not found / not authorized for the caller.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } }, "post": { "summary": "Link an asset to a ticket", "description": "Creates an asset_associations row (entity_type=ticket) linking the asset in the request body to the path ticket. Same table GET /api/v1/tickets/{id}/assets and the asset detail UI read, so the link is visible from both sides. relationship_type defaults to affected. The route is authenticated and tenant-scoped via ApiBaseController.authenticate + runWithTenant; it enforces ticket:update plus a per-ticket authorization-kernel check (assertTicketReadAllowed) AND asset:read (the asset is referenced). The response uses the apiMiddleware envelope ({ data } with no top-level success field). Both the ticket and asset must exist (404 otherwise); a duplicate link returns 409.", "operationId": "linkAssetToTicket", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-rbac-resource": "ticket", "x-rbac-action": "update", "x-rbac-additional-required": "asset:read", "x-per-record-authorization": "assertTicketReadAllowed" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "requestBody": { "description": "The asset to link plus optional relationship_type and notes.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TicketAssetLinkRequest" }, "description": "The asset to link plus optional relationship_type and notes." } } }, "responses": { "201": { "description": "Asset linked. Returns the inserted asset_associations row.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TicketAssetLinkResponse" } } } }, "400": { "description": "Request body validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks ticket:update (or per-ticket authorization) or asset:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Ticket or asset not found in the tenant.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "409": { "description": "Asset is already linked to this ticket.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}/tickets/{ticketId}": { "delete": { "summary": "Unlink a ticket from an asset", "description": "Deletes the asset_associations row (entity_type=ticket) linking the path asset and ticket for the authenticated tenant. The route is authenticated and tenant-scoped via withApiKeyRouteAuth and enforces asset:update plus ticket:read. Returns 404 when no such link exists.", "operationId": "unlinkTicketFromAsset", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-current-auth-wiring-missing": false, "x-rbac-resource": "asset", "x-rbac-action": "update", "x-rbac-additional-required": "ticket:read", "x-idempotent": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Ticket UUID from tickets.ticket_id." }, "required": true, "description": "Ticket UUID from tickets.ticket_id.", "name": "ticketId", "in": "path" } ], "responses": { "204": { "description": "Link removed." }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks asset:update or ticket:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Asset-ticket association not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/tickets/{id}/assets/{assetId}": { "delete": { "summary": "Unlink an asset from a ticket", "description": "Deletes the asset_associations row (entity_type=ticket) linking the path asset and ticket for the authenticated tenant. Authenticated and tenant-scoped via ApiBaseController.authenticate + runWithTenant; enforces ticket:update plus the per-ticket authorization check and asset:read. Returns 404 when no such link exists.", "operationId": "unlinkAssetFromTicket", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-rbac-resource": "ticket", "x-rbac-action": "update", "x-rbac-additional-required": "asset:read", "x-per-record-authorization": "assertTicketReadAllowed", "x-idempotent": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Ticket UUID from tickets.ticket_id." }, "required": true, "description": "Ticket UUID from tickets.ticket_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "assetId", "in": "path" } ], "responses": { "204": { "description": "Link removed." }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks ticket:update (or per-ticket authorization) or asset:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Asset-ticket association not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}/notes": { "get": { "summary": "Get asset notes", "description": "Returns the BlockNote content of the asset notes document, or an empty result when no notes exist.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "asset", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Asset notes content.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetNotesResponse" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks the required asset permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Asset not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } }, "put": { "summary": "Update asset notes", "description": "Creates or updates the BlockNote notes document linked to the asset. Returns the document id.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "asset", "x-rbac-action": "update" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetNotesUpdateBody" } } } }, "responses": { "200": { "description": "Notes saved.", "content": { "application/json": { "schema": { "type": "object", "properties": { "document_id": { "type": "string", "format": "uuid" } }, "required": [ "document_id" ] } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks the required asset permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Asset not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } }, "delete": { "summary": "Delete asset notes", "description": "Unlinks the notes document from the asset. Pass delete_document=true to also hard-delete the document and its block content.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "asset", "x-rbac-action": "update" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "delete_document", "in": "query" } ], "responses": { "204": { "description": "Notes unlinked/deleted." }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks the required asset permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Asset not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}/rmm": { "get": { "summary": "Get cached RMM data", "description": "Returns the cached RMM device data synced from the RMM provider. Enterprise Edition feature — on Community Edition this returns null.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "asset", "x-rbac-action": "read", "x-edition-feature": "ee-rmm", "x-ce-behavior": "returns null or success:false EE-required message" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Cached RMM data (null on CE).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetRmmResponse" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks the required asset permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Asset not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}/rmm/reboot": { "post": { "summary": "Reboot device via RMM", "description": "Sends a reboot command to the RMM-managed device. Enterprise Edition feature — on Community Edition this returns { success:false } with an EE-required message.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "asset", "x-rbac-action": "update", "x-edition-feature": "ee-rmm", "x-ce-behavior": "returns null or success:false EE-required message" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Reboot result.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetRmmActionResponse" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks the required asset permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Asset not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}/rmm/refresh": { "post": { "summary": "Refresh RMM data for device", "description": "Triggers a single-device RMM sync and returns the refreshed data. Enterprise Edition feature — on Community Edition this returns null.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "asset", "x-rbac-action": "update", "x-edition-feature": "ee-rmm", "x-ce-behavior": "returns null or success:false EE-required message" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Refreshed RMM data (null on CE).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetRmmResponse" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks the required asset permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Asset not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}/rmm/remote-control": { "get": { "summary": "Get remote-control session URL", "description": "Generates a remote-control URL for the device using the protocol given by the type query param. Enterprise Edition feature — on Community Edition this returns null.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "asset", "x-rbac-action": "read", "x-edition-feature": "ee-rmm", "x-ce-behavior": "returns null or success:false EE-required message" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "enum": [ "splashtop", "teamviewer", "vnc", "rdp", "shell" ], "description": "Remote-control protocol (default splashtop)." }, "required": false, "description": "Remote-control protocol (default splashtop).", "name": "type", "in": "query" } ], "responses": { "200": { "description": "Remote-control URL (null on CE).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetRemoteControlResponse" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks the required asset permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Asset not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}/rmm/script": { "post": { "summary": "Run a script via RMM", "description": "Runs the script identified by scriptId on the RMM-managed device. Enterprise Edition feature — on Community Edition this returns { success:false } with an EE-required message.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "asset", "x-rbac-action": "update", "x-edition-feature": "ee-rmm", "x-ce-behavior": "returns null or success:false EE-required message" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetRmmScriptBody" } } } }, "responses": { "200": { "description": "Script run result.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetRmmActionResponse" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks the required asset permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Asset not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}/software": { "get": { "summary": "List asset software inventory", "description": "Returns the paginated software inventory for the asset (from the normalized software tables), with filters for category, software_type, search, and include_uninstalled, plus a summary of totals.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "asset", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" }, { "schema": { "type": "integer", "minimum": 1 }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "integer", "minimum": 1, "maximum": 200 }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "category", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "software_type", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_uninstalled", "in": "query" } ], "responses": { "200": { "description": "Asset software inventory.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetSoftwareResponse" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks the required asset permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Asset not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/assets/{id}/summary": { "get": { "summary": "Get asset summary metrics", "description": "Returns computed asset metrics: health status/reason, security status/issues, warranty status and days remaining, and open ticket count.", "tags": [ "Assets" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "asset", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Asset UUID from assets.asset_id." }, "required": true, "description": "Asset UUID from assets.asset_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Asset summary metrics.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetSummaryResponse" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetMiddlewareUnauthorizedResponse" } } } }, "403": { "description": "Caller lacks the required asset permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "404": { "description": "Asset not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetApiErrorEnvelope" } } } } } } }, "/api/v1/automation/executions": { "get": { "summary": "List automation executions", "description": "Returns a paginated list of automation executions for the authenticated tenant. Authentication uses x-api-key with optional x-tenant-id; the controller validates the API key, resolves the user and tenant, runs under tenant context, and requires automation:read permission. The service forces tenant into the execution query conditions. Current service code appears to wrap the execution array in another array before createPaginatedResponse, which may produce nested data in practice.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "read", "x-service-data-wrapping-bug": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Page number as a query string. Defaults to 1." }, "required": false, "description": "Page number as a query string. Defaults to 1.", "name": "page", "in": "query" }, { "schema": { "type": "string", "description": "Page size as a query string. Must parse to 1 through 100; defaults to 25." }, "required": false, "description": "Page size as a query string. Must parse to 1 through 100; defaults to 25.", "name": "limit", "in": "query" }, { "schema": { "type": "string", "description": "Accepted by shared list query validation; the service currently orders executions by started_at desc." }, "required": false, "description": "Accepted by shared list query validation; the service currently orders executions by started_at desc.", "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ], "description": "Accepted by shared list query validation." }, "required": false, "description": "Accepted by shared list query validation.", "name": "order", "in": "query" }, { "schema": { "type": "string", "description": "Accepted by shared filter validation." }, "required": false, "description": "Accepted by shared filter validation.", "name": "search", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "required": false, "description": "Accepted by shared filter validation.", "name": "created_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "required": false, "description": "Accepted by shared filter validation.", "name": "created_to", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "required": false, "description": "Accepted by shared filter validation.", "name": "updated_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "required": false, "description": "Accepted by shared filter validation.", "name": "updated_to", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ], "description": "Accepted by shared filter validation and transformed to boolean." }, "required": false, "description": "Accepted by shared filter validation and transformed to boolean.", "name": "is_active", "in": "query" }, { "schema": { "type": "string", "format": "uuid", "description": "Filter executions by parent automation rule UUID." }, "required": false, "description": "Filter executions by parent automation rule UUID.", "name": "automation_rule_id", "in": "query" }, { "schema": { "type": "string", "enum": [ "pending", "running", "completed", "failed", "cancelled", "timeout", "skipped" ], "description": "Filter by execution status." }, "required": false, "description": "Filter by execution status.", "name": "status", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Filter executions started at or after this timestamp." }, "required": false, "description": "Filter executions started at or after this timestamp.", "name": "started_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Filter executions started at or before this timestamp." }, "required": false, "description": "Filter executions started at or before this timestamp.", "name": "started_to", "in": "query" }, { "schema": { "type": "number", "minimum": 0, "description": "Minimum execution duration in milliseconds." }, "required": false, "description": "Minimum execution duration in milliseconds.", "name": "duration_min_ms", "in": "query" }, { "schema": { "type": "number", "minimum": 0, "description": "Maximum execution duration in milliseconds." }, "required": false, "description": "Maximum execution duration in milliseconds.", "name": "duration_max_ms", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ], "description": "Filter by whether the execution has errors; transformed to boolean by validation." }, "required": false, "description": "Filter by whether the execution has errors; transformed to boolean by validation.", "name": "has_errors", "in": "query" }, { "schema": { "type": "string", "enum": [ "time_based", "event_based", "condition_based", "manual", "recurring", "webhook" ], "description": "Filter by automation trigger type." }, "required": false, "description": "Filter by automation trigger type.", "name": "trigger_type", "in": "query" } ], "responses": { "200": { "description": "Automation executions returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationExecutionListResponse" } } } }, "400": { "description": "Query parameter validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected automation execution listing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } } }, "/api/v1/automation/executions/{id}": { "get": { "summary": "Get automation execution", "description": "Returns one automation execution by execution_id for the authenticated tenant, with HATEOAS links to the parent rule and retry endpoint when the status is failed. Authentication uses x-api-key with optional x-tenant-id; the controller validates the API key, resolves tenant context, validates the UUID path parameter, and requires automation:read permission. The service scopes lookup by execution_id and tenant. Missing executions currently throw a generic Error and may surface as 500 rather than 404.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "read", "x-no-clean-not-found-currently": true, "x-generic-resource-links-may-include-unimplemented-methods": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Automation execution UUID from automation_executions.execution_id." }, "required": true, "description": "Automation execution UUID from automation_executions.execution_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Automation execution returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationExecutionResponse" } } } }, "400": { "description": "Invalid execution UUID format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "404": { "description": "Intended not-found response for a missing execution; current service may surface this as 500.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected failure, including current generic not-found errors.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } } }, "/api/v1/automation/executions/{id}/retry": { "post": { "summary": "Retry automation execution", "description": "Retries a failed automation execution. The controller authenticates with x-api-key, requires automation:execute permission, validates the execution UUID, resets a failed execution to pending, clears completion/error counters, queues the execution, publishes automation.execution.retried, and returns the updated execution with links. The service only allows status=failed; missing executions and non-failed executions currently throw generic Errors and may surface as 500 rather than 404 or 400. The queueExecution helper is currently a stub.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "execute", "x-requires-current-status": "failed", "x-queues-execution-stubbed": true, "x-publishes-event": "automation.execution.retried", "x-no-clean-not-found-currently": true, "x-not-failed-error-is-500-currently": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Automation execution UUID from automation_executions.execution_id." }, "required": true, "description": "Automation execution UUID from automation_executions.execution_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Execution reset and queued for retry.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationExecutionResponse" } } } }, "400": { "description": "Invalid execution UUID format, or intended response when execution is not failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:execute permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "404": { "description": "Intended not-found response for a missing execution; current service may surface this as 500.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected failure, including current generic not-found or not-failed errors.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } } }, "/api/v1/automation/rules": { "get": { "summary": "List automation rules", "description": "Returns a paginated list of automation rules scoped to the authenticated tenant. Authentication uses x-api-key with optional x-tenant-id, then RBAC requires automation:read. The controller validates query params with automationRulesListSchema, parses page/limit from the URL, and the service enforces tenant filtering on automation_rules.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "read", "x-id-provenance": { "rule_id": "automation_rules.rule_id", "tenant": "automation_rules.tenant" } }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Page number as a query string. Defaults to 1." }, "required": false, "description": "Page number as a query string. Defaults to 1.", "name": "page", "in": "query" }, { "schema": { "type": "string", "description": "Page size as a query string. Must parse to 1 through 100; defaults to 25." }, "required": false, "description": "Page size as a query string. Must parse to 1 through 100; defaults to 25.", "name": "limit", "in": "query" }, { "schema": { "type": "string", "description": "Accepted by shared list query validation; service currently orders rules by created_at desc." }, "required": false, "description": "Accepted by shared list query validation; service currently orders rules by created_at desc.", "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ], "description": "Accepted by shared list query validation." }, "required": false, "description": "Accepted by shared list query validation.", "name": "order", "in": "query" }, { "schema": { "type": "string", "description": "Accepted by shared filter validation." }, "required": false, "description": "Accepted by shared filter validation.", "name": "search", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "required": false, "description": "Accepted by shared filter validation.", "name": "created_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "required": false, "description": "Accepted by shared filter validation.", "name": "created_to", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "required": false, "description": "Accepted by shared filter validation.", "name": "updated_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "required": false, "description": "Accepted by shared filter validation.", "name": "updated_to", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ], "description": "Accepted by shared filter validation and transformed to boolean." }, "required": false, "description": "Accepted by shared filter validation and transformed to boolean.", "name": "is_active", "in": "query" }, { "schema": { "type": "string", "description": "Optional rule name filter." }, "required": false, "description": "Optional rule name filter.", "name": "name", "in": "query" }, { "schema": { "type": "string", "enum": [ "active", "inactive", "draft", "error" ], "description": "Filter by automation rule status." }, "required": false, "description": "Filter by automation rule status.", "name": "status", "in": "query" }, { "schema": { "type": "string", "enum": [ "time_based", "event_based", "condition_based", "manual", "recurring", "webhook" ], "description": "Filter by trigger type." }, "required": false, "description": "Filter by trigger type.", "name": "trigger_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "low", "normal", "high", "critical" ], "description": "Filter by priority level." }, "required": false, "description": "Filter by priority level.", "name": "priority", "in": "query" }, { "schema": { "type": "string", "description": "Filter template category." }, "required": false, "description": "Filter template category.", "name": "template_category", "in": "query" }, { "schema": { "type": "string", "format": "uuid", "description": "Filter by creator user UUID from automation_rules.created_by." }, "required": false, "description": "Filter by creator user UUID from automation_rules.created_by.", "name": "created_by", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Filter for rules last executed at or after timestamp." }, "required": false, "description": "Filter for rules last executed at or after timestamp.", "name": "last_executed_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Filter for rules last executed at or before timestamp." }, "required": false, "description": "Filter for rules last executed at or before timestamp.", "name": "last_executed_to", "in": "query" }, { "schema": { "type": "integer", "minimum": 0, "description": "Minimum execution count filter." }, "required": false, "description": "Minimum execution count filter.", "name": "execution_count_min", "in": "query" }, { "schema": { "type": "integer", "minimum": 0, "description": "Maximum execution count filter." }, "required": false, "description": "Maximum execution count filter.", "name": "execution_count_max", "in": "query" } ], "responses": { "200": { "description": "Automation rules returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationRuleListResponse" } } } }, "400": { "description": "Query parameter validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected automation rules listing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } }, "post": { "summary": "Create automation rule", "description": "Creates a tenant-scoped automation rule in automation_rules. Authentication uses x-api-key with optional x-tenant-id; RBAC requires automation:create. The controller validates request body via createAutomationRuleSchema, and the service validates trigger/action config, inserts the rule with crypto.randomUUID generated rule_id, and emits automation.rule.created + audit log events.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "create", "x-id-provenance": { "rule_id": "automation_rules.rule_id (crypto.randomUUID)", "created_by": "API key context userId" }, "x-publishes-event": "automation.rule.created", "x-audit-log-action": "automation_rule_created" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateAutomationRuleRequest" } } } }, "responses": { "201": { "description": "Automation rule created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationRuleResponse" } } } }, "400": { "description": "Body validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:create permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected automation rule creation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } } }, "/api/v1/automation/rules/{id}": { "get": { "summary": "Get automation rule", "description": "Returns one automation rule by rule_id for the authenticated tenant with generated links and execution statistics. Authentication uses x-api-key with optional x-tenant-id; RBAC requires automation:read. The service scopes by {rule_id, tenant}. Missing rules currently throw generic Error and surface as 500 rather than 404.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "read", "x-id-provenance": { "rule_id": "automation_rules.rule_id" }, "x-no-clean-not-found-currently": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Automation rule UUID from automation_rules.rule_id." }, "required": true, "description": "Automation rule UUID from automation_rules.rule_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Automation rule returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationRuleResponse" } } } }, "400": { "description": "Invalid rule UUID format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "404": { "description": "Intended not-found response for missing rules; current service may surface this as 500.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected failure, including current generic not-found errors.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } }, "put": { "summary": "Update automation rule", "description": "Updates an automation rule by rule_id for the authenticated tenant. Authentication uses x-api-key with optional x-tenant-id; RBAC requires automation:update. The controller validates partial updates with updateAutomationRuleSchema and the service revalidates trigger/action config when changed, updates automation_rules, and emits automation.rule.updated + audit log events. Missing rules currently surface as 500.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "update", "x-publishes-event": "automation.rule.updated", "x-audit-log-action": "automation_rule_updated", "x-no-clean-not-found-currently": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Automation rule UUID from automation_rules.rule_id." }, "required": true, "description": "Automation rule UUID from automation_rules.rule_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateAutomationRuleRequest" } } } }, "responses": { "200": { "description": "Automation rule updated successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationRuleResponse" } } } }, "400": { "description": "Invalid UUID format or body validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:update permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "404": { "description": "Intended not-found response for missing rules; current service may surface this as 500.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected failure, including current generic not-found errors.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } }, "delete": { "summary": "Delete automation rule", "description": "Deletes one automation rule by rule_id for the authenticated tenant. Authentication uses x-api-key with optional x-tenant-id; RBAC requires automation:delete. The service blocks deletion when pending/running executions exist, unschedules active rules, deletes automation_rules row, and emits automation.rule.deleted + audit log events. Missing rules and delete constraints currently bubble as 500 errors.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "delete", "x-publishes-event": "automation.rule.deleted", "x-audit-log-action": "automation_rule_deleted", "x-no-clean-not-found-currently": true, "x-delete-blocked-when-running-executions": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Automation rule UUID from automation_rules.rule_id." }, "required": true, "description": "Automation rule UUID from automation_rules.rule_id.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Automation rule deleted successfully." }, "400": { "description": "Invalid rule UUID format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:delete permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "404": { "description": "Intended not-found response for missing rules; current service may surface this as 500.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected failure, including running-execution constraints and generic not-found errors.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } } }, "/api/v1/automation/rules/{id}/execute": { "post": { "summary": "Execute automation rule manually", "description": "Starts a manual execution for one automation rule identified by path rule_id. Authentication uses x-api-key with optional x-tenant-id; RBAC requires automation:execute. The current request schema requires automation_rule_id in the body, but controller/service execute the path rule ID and do not cross-check the body ID. For dry_run=false the service creates automation_executions row and queues execution via a currently stubbed queue helper.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "execute", "x-body-automation-rule-id-not-used": true, "x-id-provenance": { "execution_id": "automation_executions.execution_id (crypto.randomUUID)", "automation_rule_id": "URL path param /rules/{id}" }, "x-publishes-event": "automation.execution.started", "x-audit-log-action": "automation_execution_started", "x-queues-execution-stubbed": true, "x-no-clean-not-found-currently": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Automation rule UUID from automation_rules.rule_id." }, "required": true, "description": "Automation rule UUID from automation_rules.rule_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ManualExecutionRequest" } } } }, "responses": { "201": { "description": "Manual execution created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationExecutionResponse" } } } }, "400": { "description": "Invalid UUID format or body validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:execute permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "404": { "description": "Intended not-found response for missing rules; current service may surface this as 500.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected failure, including current generic not-found and inactive-rule errors.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } } }, "/api/v1/automation/rules/bulk-status": { "post": { "summary": "Bulk update automation rule status", "description": "Updates status for up to 100 automation rules under the authenticated tenant. Authentication uses x-api-key with optional x-tenant-id; RBAC requires automation:update. The controller validates ids/status with bulkStatusUpdateSchema and calls updateAutomationRule per id. Failures are collected into an errors array and still return HTTP 200 with partial-success details.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "update", "x-partial-failures-return-200": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkStatusUpdateRequest" } } } }, "responses": { "200": { "description": "Bulk status update attempted for all ids; includes per-id errors.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkStatusUpdateResponse" } } } }, "400": { "description": "Body validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:update permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected controller or service failure before result aggregation.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } } }, "/api/v1/automation/rules/bulk-execute": { "post": { "summary": "Bulk execute automation rules", "description": "Starts execution attempts for up to 20 automation rules for the authenticated tenant. Authentication uses x-api-key with optional x-tenant-id; RBAC requires automation:execute. The controller validates bulkExecutionSchema and calls executeAutomationRule per id. sequential_execution=true runs serially; otherwise Promise.all is used. Per-rule errors are returned in a 200 response.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "execute", "x-partial-failures-return-200": true, "x-sequential-execution-supported": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkExecuteRequest" } } } }, "responses": { "200": { "description": "Bulk execution attempted for all ids; includes started count and per-id errors.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkExecuteResponse" } } } }, "400": { "description": "Body validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:execute permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected controller or service failure before result aggregation.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } } }, "/api/v1/automation/templates": { "get": { "summary": "List automation templates", "description": "Returns a paginated list of automation templates for the authenticated tenant. Authentication uses x-api-key with optional x-tenant-id; RBAC requires automation:read. The controller validates query params with templatesListSchema and the service filters automation_templates by tenant. Current service code wraps template array before createPaginatedResponse, which can produce nested data in practice.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "read", "x-service-data-wrapping-bug": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Page number as a query string. Defaults to 1." }, "required": false, "description": "Page number as a query string. Defaults to 1.", "name": "page", "in": "query" }, { "schema": { "type": "string", "description": "Page size as a query string. Must parse to 1 through 100; defaults to 25." }, "required": false, "description": "Page size as a query string. Must parse to 1 through 100; defaults to 25.", "name": "limit", "in": "query" }, { "schema": { "type": "string", "description": "Accepted by shared list query validation; service currently orders templates by created_at desc." }, "required": false, "description": "Accepted by shared list query validation; service currently orders templates by created_at desc.", "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ], "description": "Accepted by shared list query validation." }, "required": false, "description": "Accepted by shared list query validation.", "name": "order", "in": "query" }, { "schema": { "type": "string", "description": "Accepted by shared filter validation." }, "required": false, "description": "Accepted by shared filter validation.", "name": "search", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "required": false, "description": "Accepted by shared filter validation.", "name": "created_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "required": false, "description": "Accepted by shared filter validation.", "name": "created_to", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "required": false, "description": "Accepted by shared filter validation.", "name": "updated_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Accepted by shared filter validation." }, "required": false, "description": "Accepted by shared filter validation.", "name": "updated_to", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ], "description": "Accepted by shared filter validation and transformed to boolean." }, "required": false, "description": "Accepted by shared filter validation and transformed to boolean.", "name": "is_active", "in": "query" }, { "schema": { "type": "string", "description": "Filter templates by category." }, "required": false, "description": "Filter templates by category.", "name": "category", "in": "query" }, { "schema": { "type": "string", "description": "Filter templates by compatibility version tag." }, "required": false, "description": "Filter templates by compatibility version tag.", "name": "compatible_version", "in": "query" }, { "schema": { "type": "string", "description": "Filter templates by author/user id value saved on the template." }, "required": false, "description": "Filter templates by author/user id value saved on the template.", "name": "author", "in": "query" }, { "schema": { "type": "integer", "minimum": 0, "description": "Minimum usage_count filter." }, "required": false, "description": "Minimum usage_count filter.", "name": "usage_count_min", "in": "query" }, { "schema": { "type": "integer", "minimum": 0, "description": "Maximum usage_count filter." }, "required": false, "description": "Maximum usage_count filter.", "name": "usage_count_max", "in": "query" } ], "responses": { "200": { "description": "Automation templates returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationTemplateListResponse" } } } }, "400": { "description": "Query parameter validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected automation templates listing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } }, "post": { "summary": "Create automation template from rule", "description": "Creates a template row by copying one source automation rule into automation_templates.template_config. Authentication uses x-api-key with optional x-tenant-id; RBAC requires automation:create. The controller validates createTemplateFromRuleSchema and service emits automation.template.created + audit log events. Missing source rules currently throw generic Error and surface as 500.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "create", "x-id-provenance": { "template_id": "automation_templates.template_id (crypto.randomUUID)", "automation_rule_id": "automation_rules.rule_id" }, "x-publishes-event": "automation.template.created", "x-audit-log-action": "automation_template_created", "x-no-clean-not-found-currently": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateAutomationTemplateRequest" } } } }, "responses": { "201": { "description": "Automation template created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationTemplateResponse" } } } }, "400": { "description": "Body validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:create permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "404": { "description": "Intended not-found response for missing source rules; current service may surface this as 500.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected failure, including current generic source-rule not-found errors.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } } }, "/api/v1/automation/templates/{id}": { "get": { "summary": "Get automation template", "description": "Returns one automation template by template_id for the authenticated tenant. Authentication uses x-api-key with optional x-tenant-id; RBAC requires automation:read. Service lookup is scoped by {template_id, tenant}. Missing templates currently throw generic Error and surface as 500 rather than 404.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "read", "x-id-provenance": { "template_id": "automation_templates.template_id" }, "x-no-clean-not-found-currently": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Automation template UUID from automation_templates.template_id." }, "required": true, "description": "Automation template UUID from automation_templates.template_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Automation template returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationTemplateResponse" } } } }, "400": { "description": "Invalid template UUID format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "404": { "description": "Intended not-found response for missing templates; current service may surface this as 500.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected failure, including current generic not-found errors.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } } }, "/api/v1/automation/templates/{id}/use": { "post": { "summary": "Create automation rule from template", "description": "Uses one template_id to create a new automation rule under the authenticated tenant. Authentication uses x-api-key with optional x-tenant-id; RBAC requires automation:create. The controller validates `{ variables }`, then service loads template, applies substitutions into template_config, creates rule via createAutomationRule, and increments automation_templates.usage_count/last_used. Missing templates surface as 500.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "create", "x-id-provenance": { "template_id": "automation_templates.template_id", "rule_id": "automation_rules.rule_id (crypto.randomUUID from createAutomationRule)" }, "x-uses-template-config-clone": true, "x-no-clean-not-found-currently": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Automation template UUID from automation_templates.template_id." }, "required": true, "description": "Automation template UUID from automation_templates.template_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UseAutomationTemplateRequest" } } } }, "responses": { "201": { "description": "Automation rule created from template.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationRuleResponse" } } } }, "400": { "description": "Invalid template UUID format or body validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:create permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "404": { "description": "Intended not-found response for missing templates; current service may surface this as 500.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected failure, including current generic template not-found errors.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } } }, "/api/v1/automation/statistics": { "get": { "summary": "Get automation statistics", "description": "Returns tenant-scoped automation aggregate counts (rules and executions) plus HATEOAS links. Authentication uses x-api-key with optional x-tenant-id; RBAC requires automation:read. The service computes counts from automation_rules and automation_executions plus period-based execution totals and success rate.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "read", "x-analytics-from-tables": [ "automation_rules", "automation_executions" ] }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Automation statistics returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationStatisticsResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected automation statistics failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } } }, "/api/v1/automation/performance": { "get": { "summary": "Get automation performance metrics", "description": "Returns performance metrics calculated by AutomationService.calculatePerformanceMetrics for the authenticated tenant. Authentication uses x-api-key with optional x-tenant-id; RBAC requires automation:read. Query is validated with performanceMetricsRequestSchema; because validateQueryParams maps URL values to strings, array filters (rule_ids, metrics) currently have parsing limitations and may fail validation unless encoded specially.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "read", "x-query-array-parsing-gap": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "date-time", "description": "Optional lower bound for performance window." }, "required": false, "description": "Optional lower bound for performance window.", "name": "date_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Optional upper bound for performance window." }, "required": false, "description": "Optional upper bound for performance window.", "name": "date_to", "in": "query" }, { "schema": { "type": "string", "enum": [ "hour", "day", "week", "month" ], "description": "Aggregation bucket size; defaults to day." }, "required": false, "description": "Aggregation bucket size; defaults to day.", "name": "group_by", "in": "query" }, { "schema": { "type": "string", "description": "Raw query value copied from URL. The controller expects an array UUID schema but validateQueryParams passes string values, so array filters currently require custom caller-side encoding or fail validation." }, "required": false, "description": "Raw query value copied from URL. The controller expects an array UUID schema but validateQueryParams passes string values, so array filters currently require custom caller-side encoding or fail validation.", "name": "rule_ids", "in": "query" }, { "schema": { "type": "string", "description": "Raw query value copied from URL. The controller expects an array enum schema but validateQueryParams passes string values, so array filters currently require custom caller-side encoding or fail validation." }, "required": false, "description": "Raw query value copied from URL. The controller expects an array enum schema but validateQueryParams passes string values, so array filters currently require custom caller-side encoding or fail validation.", "name": "metrics", "in": "query" } ], "responses": { "200": { "description": "Automation performance payload returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationPerformanceResponse" } } } }, "400": { "description": "Query parameter validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected performance metrics failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } } }, "/api/v1/automation/meta": { "get": { "summary": "Get automation metadata", "description": "Returns a static reference catalog of automation enum values: trigger types, action types, rule statuses, execution statuses, priority levels, and condition operators. Authentication uses x-api-key with optional x-tenant-id; the controller validates the API key, resolves tenant context, and requires automation:read permission. The response is hardcoded and performs no service or database lookup beyond authentication/RBAC.", "tags": [ "Automation" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "automation", "x-rbac-action": "read", "x-static-reference-data": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Automation metadata returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationMetaResponse" } } } }, "401": { "description": "API key is missing, invalid, expired, over limit, or the key user was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "403": { "description": "Authenticated user lacks automation:read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } }, "500": { "description": "Unexpected automation metadata failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AutomationErrorResponse" } } } } } } }, "/api/billing/licence-count": { "get": { "summary": "Get tenant licence usage", "description": "Server-to-server webhook endpoint used by the billing service to query current licence usage for a tenant. The caller supplies tenant_id and an x-webhook-signature header containing HMAC-SHA256(\"GET:\", ALGA_WEBHOOK_SECRET). The response reports the configured licensed internal-user limit, active internal-user count, calculated remaining seats, and last licence update timestamp.", "tags": [ "Billing" ], "security": [ { "WebhookSignatureAuth": [] } ], "extensions": { "x-webhook-secured": true, "x-server-to-server": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Tenant identifier to query. This must match a tenants.tenant record." }, "required": true, "description": "Tenant identifier to query. This must match a tenants.tenant record.", "name": "tenant_id", "in": "query" }, { "schema": { "type": "string", "description": "HMAC-SHA256 hex digest proving the caller knows ALGA_WEBHOOK_SECRET." }, "required": true, "description": "HMAC-SHA256 hex digest proving the caller knows ALGA_WEBHOOK_SECRET.", "name": "x-webhook-signature", "in": "header" } ], "responses": { "200": { "description": "Licence usage returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LicenceUsageResponse" } } } }, "400": { "description": "Missing tenant_id query parameter.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LicenceCountErrorResponse" } } } }, "401": { "description": "Webhook signature missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LicenceCountErrorResponse" } } } }, "404": { "description": "Tenant not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LicenceCountErrorResponse" } } } }, "500": { "description": "Unexpected internal error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LicenceCountErrorResponse" } } } } } }, "post": { "summary": "Update tenant licensed user count", "description": "Server-to-server webhook endpoint used by the billing service to synchronize a tenant's licensed internal-user count from a Stripe subscription quantity. The x-webhook-signature header must be the HMAC-SHA256 hex digest of the raw JSON request body using ALGA_WEBHOOK_SECRET. When event_id is provided, the handler treats tenants.stripe_event_id as an idempotency marker and skips duplicate Stripe events.", "tags": [ "Billing" ], "security": [ { "WebhookSignatureAuth": [] } ], "extensions": { "x-webhook-secured": true, "x-server-to-server": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "HMAC-SHA256 hex digest proving the caller knows ALGA_WEBHOOK_SECRET." }, "required": true, "description": "HMAC-SHA256 hex digest proving the caller knows ALGA_WEBHOOK_SECRET.", "name": "x-webhook-signature", "in": "header" } ], "requestBody": { "description": "Licence count update payload from the billing service.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LicenceCountUpdateRequest" }, "description": "Licence count update payload from the billing service." } } }, "responses": { "200": { "description": "Licence count update accepted, or duplicate event safely ignored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LicenceCountUpdateResponse" } } } }, "400": { "description": "tenant_id or license_count is missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LicenceCountErrorResponse" } } } }, "401": { "description": "Webhook signature missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LicenceCountErrorResponse" } } } }, "404": { "description": "Tenant not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LicenceCountErrorResponse" } } } }, "500": { "description": "Unexpected internal error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LicenceCountErrorResponse" } } } } } } }, "/api/chat/stream/title": { "post": { "summary": "Generate chat title stream", "description": "Enterprise-only AI endpoint that generates a short title from conversation messages. Requires a valid Auth.js session and the aiAssistant experimental feature enabled for the tenant. The handler sends the messages to OpenRouter and returns Server-Sent Events; the implementation emits a generated title event and a completion event rather than a conventional JSON response.", "tags": [ "Chat" ], "security": [ { "SessionCookieAuth": [] } ], "extensions": { "x-edition-gated": "enterprise", "x-feature-flag": "aiAssistant", "x-streaming": "sse" }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Conversation messages to summarize into a title.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatTitleStreamRequest" }, "description": "Conversation messages to summarize into a title." } } }, "responses": { "200": { "description": "SSE stream of title-generation events.", "content": { "text/event-stream": { "schema": { "$ref": "#/components/schemas/ChatStreamEvent" } } } }, "403": { "description": "AI Assistant feature is not enabled for the tenant.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatStreamError" } } } }, "404": { "description": "Chat streaming is not available in this edition.", "content": { "application/json": { "schema": { "type": "string", "description": "Edition-gate error message." } } } }, "500": { "description": "Malformed request, missing model credentials, or another internal streaming failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatStreamError" } } } } } } }, "/api/chat/stream/{slug}": { "get": { "summary": "Get chat stream placeholder", "description": "Placeholder GET handler for the chat stream catch-all route. The slug path segment is accepted by the Next.js route but is not inspected. The handler currently returns the plain text string \"Hello World\" and does not require authentication.", "tags": [ "Chat" ], "security": [], "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Catch-all chat stream path segment. The current handler accepts the value but does not use it." }, "required": true, "description": "Catch-all chat stream path segment. The current handler accepts the value but does not use it.", "name": "slug", "in": "path" } ], "responses": { "200": { "description": "Plain text placeholder response.", "content": { "text/plain": { "schema": { "type": "string", "description": "Always returns Hello World." } } } } } }, "post": { "summary": "Stream AI chat response", "description": "Enterprise-only AI chat endpoint that accepts conversation messages and returns an assistant response via Server-Sent Events. Requires a valid Auth.js session and the aiAssistant experimental feature enabled for the tenant. The slug path segment is accepted by the catch-all route but is not used by the implementation. API-key authentication is skipped for /api/chat; tenant context comes from the session.", "tags": [ "Chat" ], "security": [ { "SessionCookieAuth": [] } ], "extensions": { "x-edition-gated": "enterprise", "x-feature-flag": "aiAssistant", "x-streaming": "sse" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Catch-all chat stream path segment. The current handler accepts the value but does not use it." }, "required": true, "description": "Catch-all chat stream path segment. The current handler accepts the value but does not use it.", "name": "slug", "in": "path" } ], "requestBody": { "description": "Conversation messages and optional legacy metadata for the chat model.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatStreamRequest" }, "description": "Conversation messages and optional legacy metadata for the chat model." } } }, "responses": { "200": { "description": "SSE stream of chat response events. Each data frame contains content and type.", "content": { "text/event-stream": { "schema": { "$ref": "#/components/schemas/ChatStreamEvent" } } } }, "403": { "description": "AI Assistant feature is not enabled for the tenant.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatStreamError" } } } }, "404": { "description": "Chat streaming is not available in this edition.", "content": { "application/json": { "schema": { "type": "string", "description": "Edition-gate error message." } } } }, "500": { "description": "Malformed request, missing OpenRouter API key, or another internal streaming failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatStreamError" } } } } } } }, "/api/v1/clients": { "get": { "summary": "List clients", "description": "Inherited ApiBaseController list route for clients. Requires API-key auth and client:read permission.", "tags": [ "Clients" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "client", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "created_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "created_to", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "updated_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "updated_to", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "client_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "email", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "client_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "weekly", "bi-weekly", "monthly", "quarterly", "semi-annually", "annually" ] }, "required": false, "name": "billing_cycle", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_inactive", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_tax_exempt", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "account_manager_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "region_code", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "credit_balance_min", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "credit_balance_max", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_credit_limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "industry", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "company_size", "in": "query" } ], "responses": { "200": { "description": "Paginated clients returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedClientEnvelope" } } } }, "400": { "description": "Invalid query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for client read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected client listing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } }, "post": { "summary": "Create client", "description": "Inherited ApiBaseController create route for clients. Requires API-key auth and client:create permission.", "tags": [ "Clients" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "client", "x-rbac-action": "create" }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientBody" } } } }, "responses": { "201": { "description": "Client created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientEnvelope" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for client create.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected client creation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } } }, "/api/v1/clients/{id}": { "get": { "summary": "Get client", "description": "Inherited ApiBaseController get route for one client_id.", "tags": [ "Clients" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "client", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "id", "in": "path" } ], "responses": { "200": { "description": "Client returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientEnvelope" } } } }, "400": { "description": "Invalid client id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for client read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "404": { "description": "Client not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected client retrieval failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } }, "put": { "summary": "Update client", "description": "Inherited ApiBaseController update route for one client_id.", "tags": [ "Clients" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "client", "x-rbac-action": "update" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientBody" } } } }, "responses": { "200": { "description": "Client updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientEnvelope" } } } }, "400": { "description": "Invalid client id or request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for client update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "404": { "description": "Client not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected client update failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } }, "delete": { "summary": "Delete client", "description": "Inherited ApiBaseController delete route for one client_id.", "tags": [ "Clients" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "client", "x-rbac-action": "delete" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "id", "in": "path" } ], "responses": { "204": { "description": "Client deleted." }, "400": { "description": "Invalid client id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for client delete.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "404": { "description": "Client not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected client deletion failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } } }, "/api/v1/clients/stats": { "get": { "summary": "Get client stats", "description": "Client statistics route with explicit API-key validation and client:read permission check.", "tags": [ "Clients" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "client", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "responses": { "200": { "description": "Client stats returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientStatsEnvelope" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for client read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected client stats failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } } }, "/api/v1/clients/{id}/contacts": { "get": { "summary": "List contacts for client", "description": "Returns paginated contacts filtered by client_id after client existence check.", "tags": [ "Clients" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "client", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "id", "in": "path" } ], "responses": { "200": { "description": "Client contacts returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedContactEnvelope" } } } }, "400": { "description": "Invalid client id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for client read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "404": { "description": "Client not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected client contacts failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } } }, "/api/v1/clients/{id}/locations": { "get": { "summary": "List client locations", "description": "Returns locations associated with client_id after client existence check.", "tags": [ "Clients" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "client", "x-rbac-action": "read", "x-deprecated-route-commented": true }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "id", "in": "path" } ], "responses": { "200": { "description": "Client locations returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientLocationsEnvelope" } } } }, "400": { "description": "Invalid client id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for client read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "404": { "description": "Client not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected client locations failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } }, "post": { "summary": "Create client location", "description": "Creates a location row for client_id after validation and client existence check.", "tags": [ "Clients" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "client", "x-rbac-action": "update", "x-deprecated-route-commented": true }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientLocationBody" } } } }, "responses": { "201": { "description": "Client location created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientLocationEnvelope" } } } }, "400": { "description": "Invalid client id or request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for client update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "404": { "description": "Client not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected client location creation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } } }, "/api/v1/contacts": { "get": { "summary": "List contacts", "description": "Inherited ApiBaseController list route for contacts.", "tags": [ "Contacts" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "contact", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "created_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "created_to", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "updated_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "updated_to", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "full_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "email", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "phone_number", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "client_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "role", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_inactive", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_client", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "client_name", "in": "query" } ], "responses": { "200": { "description": "Paginated contacts returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedContactEnvelope" } } } }, "400": { "description": "Invalid query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for contact read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected contact listing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } }, "post": { "summary": "Create contact", "description": "Inherited ApiBaseController create route for contacts.", "tags": [ "Contacts" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "contact", "x-rbac-action": "create" }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContactBody" } } } }, "responses": { "201": { "description": "Contact created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactEnvelope" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for contact create.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected contact creation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } } }, "/api/v1/contacts/{id}": { "get": { "summary": "Get contact", "description": "Inherited ApiBaseController get route for one contact_name_id.", "tags": [ "Contacts" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "contact", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "id", "in": "path" } ], "responses": { "200": { "description": "Contact returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactEnvelope" } } } }, "400": { "description": "Invalid contact id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for contact read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "404": { "description": "Contact not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected contact retrieval failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } }, "put": { "summary": "Update contact", "description": "Inherited ApiBaseController update route for one contact_name_id.", "tags": [ "Contacts" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "contact", "x-rbac-action": "update" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContactBody" } } } }, "responses": { "200": { "description": "Contact updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactEnvelope" } } } }, "400": { "description": "Invalid contact id or request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for contact update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "404": { "description": "Contact not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected contact update failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } }, "delete": { "summary": "Delete contact", "description": "Inherited ApiBaseController delete route for one contact_name_id.", "tags": [ "Contacts" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "contact", "x-rbac-action": "delete" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "id", "in": "path" } ], "responses": { "204": { "description": "Contact deleted." }, "400": { "description": "Invalid contact id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for contact delete.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "404": { "description": "Contact not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected contact deletion failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } } }, "/api/v1/contacts/search": { "get": { "summary": "Search contacts", "description": "Runs advanced contact search with explicit API-key auth and contact:read permission check.", "tags": [ "Contacts" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "contact", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "minLength": 1 }, "required": true, "name": "query", "in": "query" }, { "schema": { "type": "string", "description": "Comma-separated field list; parsed by contactSearchSchema transform." }, "required": false, "description": "Comma-separated field list; parsed by contactSearchSchema transform.", "name": "fields", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "client_id", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_inactive", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" } ], "responses": { "200": { "description": "Contact search results returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContactSearchEnvelope" } } } }, "400": { "description": "Invalid search query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for contact read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected contact search failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } } }, "/api/v1/contacts/export": { "get": { "summary": "Export contacts", "description": "Exports contacts as CSV or JSON using explicit API-key auth and contact:read permission.", "tags": [ "Contacts" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "contact", "x-rbac-action": "read", "x-returns-csv-when-format-csv": true }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "enum": [ "csv", "json" ] }, "required": false, "name": "format", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_inactive", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "client_id", "in": "query" }, { "schema": { "type": "string", "description": "Array schema expects parsed list, but URL values arrive as string." }, "required": false, "description": "Array schema expects parsed list, but URL values arrive as string.", "name": "fields", "in": "query" } ], "responses": { "200": { "description": "Contact export response. CSV format returns text/csv body; JSON format returns standard API envelope.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContactExportEnvelope" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for contact read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected contact export failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } } }, "/api/v1/contacts/stats": { "get": { "summary": "Get contact stats", "description": "Returns contact statistics using explicit API-key auth and contact:read permission.", "tags": [ "Contacts" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "contact", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "responses": { "200": { "description": "Contact stats returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContactStatsEnvelope" } } } }, "401": { "description": "API key missing/invalid or key user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "403": { "description": "Permission denied for contact read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unexpected contact stats failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } } }, "/api/v1/client-contract-lines": { "get": { "summary": "List client contract lines", "description": "Lists client contract lines — contract_lines rows that belong to client contracts (joined via contract_id), with the owning client_id and client_contract_id surfaced. Supports client_id, contract_line_id, service_category, is_active, has_custom_rate, and start/end-date-range filters plus page/limit pagination. Authenticated and tenant-scoped via withApiKeyRouteAuth.", "tags": [ "Client Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "created_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "created_to", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "updated_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "updated_to", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "client_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "contract_line_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "service_category", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_active", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_custom_rate", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_contractd", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "start_date_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "start_date_to", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "end_date_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "end_date_to", "in": "query" } ], "responses": { "200": { "description": "Paginated client contract lines returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContractLineListEnvelope" } } } }, "400": { "description": "Invalid query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unhandled failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } }, "post": { "summary": "Assign contract line to client", "description": "Assigns one contract line to a client by cloning a template line into the client contract, using createClientContractLineSchema validation. Authenticated and tenant-scoped via withApiKeyRouteAuth.", "tags": [ "Client Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true, "x-id-provenance": { "client_contract_line_id": "contract_lines.contract_line_id (client-owned line model)" } }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContractLineBody" } } } }, "responses": { "201": { "description": "Client contract line assignment created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContractLineEnvelope" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unhandled assignment failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } } }, "/api/v1/client-contract-lines/{id}": { "delete": { "summary": "Unassign contract line from client", "description": "Deactivates the client-owned contract line for the provided id. Authenticated and tenant-scoped via withApiKeyRouteAuth.", "tags": [ "Client Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-request-context-required": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "id", "in": "path" } ], "responses": { "204": { "description": "Client contract line unassigned." }, "400": { "description": "Invalid assignment id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } }, "500": { "description": "Unassignment failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientContactApiError" } } } } } } }, "/api/v1/contract-lines": { "get": { "summary": "List contract lines", "description": "Lists contract lines with pagination/filtering and optional include flags. Authenticated and tenant-scoped via withApiKeyRouteAuth. Query parsing/validation uses contractLineListQuerySchema, then listWithOptions reads from contract_lines for the tenant context.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "created_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "created_to", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "updated_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "updated_to", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "contract_line_name", "in": "query" }, { "schema": { "type": "string", "enum": [ "Fixed", "Hourly", "Usage" ] }, "required": false, "name": "contract_line_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "weekly", "bi-weekly", "monthly", "quarterly", "semi-annually", "annually" ] }, "required": false, "name": "billing_frequency", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_custom", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "service_category", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_services", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "clients_count_min", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "clients_count_max", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "revenue_min", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "revenue_max", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ], "description": "Controller-level include flag parsed directly from query string." }, "required": false, "description": "Controller-level include flag parsed directly from query string.", "name": "include_services", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ], "description": "Controller-level include flag parsed directly from query string." }, "required": false, "description": "Controller-level include flag parsed directly from query string.", "name": "include_usage", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ], "description": "Controller-level include flag parsed directly from query string." }, "required": false, "description": "Controller-level include flag parsed directly from query string.", "name": "include_clients", "in": "query" } ], "responses": { "200": { "description": "Contract lines returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or unhandled service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } }, "post": { "summary": "Create contract line", "description": "Creates a contract line row in contract_lines. Validation uses createContractLineSchema (including overtime/after-hours constraints). ID is generated by service with uuidv4. Route currently relies on request context presence and can fail when context is unavailable.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true, "x-id-provenance": { "contract_line_id": "contract_lines.contract_line_id (uuidv4)" } }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineBody" } } } }, "responses": { "201": { "description": "Contract line created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or unhandled service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-lines/{id}": { "get": { "summary": "Get contract line", "description": "Returns one contract line by contract_line_id, including optional related data through include query flags.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." }, "required": true, "description": "Contract line UUID from contract_lines.contract_line_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Contract line returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "404": { "description": "Contract line not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or unhandled service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } }, "put": { "summary": "Update contract line", "description": "Updates a contract line by contract_line_id using updateContractLineSchema validation.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." }, "required": true, "description": "Contract line UUID from contract_lines.contract_line_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateContractLineBody" } } } }, "responses": { "200": { "description": "Contract line updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or unhandled service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } }, "delete": { "summary": "Delete contract line", "description": "Deletes a contract line after dependency checks (in-use checks, service cleanup, contract detach).", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true, "x-returns-json-with-204-currently": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." }, "required": true, "description": "Contract line UUID from contract_lines.contract_line_id.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Deletion processed." }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or delete blocked by dependency rules.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-lines/bulk": { "post": { "summary": "Bulk create contract lines", "description": "Alias route to bulkCreateContractLines; creates multiple contract lines from `plans` array.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkCreateContractLinesBody" } } } }, "responses": { "201": { "description": "Bulk create completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or unhandled service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } }, "put": { "summary": "Bulk update contract lines", "description": "Alias route to bulkUpdateContractLines; updates multiple rows by contract_line_id.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkUpdateContractLinesBody" } } } }, "responses": { "200": { "description": "Bulk update completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or unhandled service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } }, "delete": { "summary": "Bulk delete contract lines", "description": "Alias route to bulkDeleteContractLines; deletes multiple contract lines by ID list.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkDeleteContractLinesBody" } } } }, "responses": { "200": { "description": "Bulk delete completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or delete blocked by dependency rules.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-lines/bulk/create": { "post": { "summary": "Bulk create contract lines (explicit route)", "description": "Same controller behavior as POST /api/v1/contract-lines/bulk.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkCreateContractLinesBody" } } } }, "responses": { "201": { "description": "Bulk create completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or unhandled service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-lines/bulk/update": { "put": { "summary": "Bulk update contract lines (explicit route)", "description": "Same controller behavior as PUT /api/v1/contract-lines/bulk.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkUpdateContractLinesBody" } } } }, "responses": { "200": { "description": "Bulk update completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or unhandled service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-lines/bulk/delete": { "delete": { "summary": "Bulk delete contract lines (explicit route)", "description": "Same controller behavior as DELETE /api/v1/contract-lines/bulk.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkDeleteContractLinesBody" } } } }, "responses": { "200": { "description": "Bulk delete completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or delete blocked by dependency rules.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-lines/bulk/add-services": { "post": { "summary": "Bulk add services to contract line", "description": "Adds multiple services to one contract_line_id and returns per-service success/failure results.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true, "x-partial-failures-in-response": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkAddServicesBody" } } } }, "responses": { "200": { "description": "Bulk service add completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or unhandled service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-lines/bulk/remove-services": { "delete": { "summary": "Bulk remove services from contract line", "description": "Removes multiple services from one contract_line_id and returns per-service success/failure results.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true, "x-partial-failures-in-response": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkRemoveServicesBody" } } } }, "responses": { "200": { "description": "Bulk service removal completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or unhandled service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-lines/{id}/activation": { "put": { "summary": "Set contract line activation", "description": "Activates or deactivates a contract line. Deactivation can require a reason when plan is in use.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." }, "required": true, "description": "Contract line UUID from contract_lines.contract_line_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineActivationBody" } } } }, "responses": { "200": { "description": "Activation state updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or activation business-rule failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-lines/{id}/analytics": { "get": { "summary": "Get contract line analytics", "description": "Returns analytics aggregates for one contract line.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." }, "required": true, "description": "Contract line UUID from contract_lines.contract_line_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Analytics payload returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or analytics query failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-lines/{id}/copy": { "post": { "summary": "Copy contract line", "description": "Copies an existing contract line. Current implementation validates and uses `source_contract_line_id` from body, while path `{id}` is not consumed by service logic.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true, "x-path-id-ignored-currently": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." }, "required": true, "description": "Contract line UUID from contract_lines.contract_line_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CopyContractLineBody" } } } }, "responses": { "201": { "description": "Contract line copy created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or copy operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-lines/{id}/fixed-config": { "get": { "summary": "Get fixed contract line config", "description": "Returns fixed-plan configuration for a contract line; returns 404 when not found.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." }, "required": true, "description": "Contract line UUID from contract_lines.contract_line_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Fixed config returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "404": { "description": "Fixed configuration not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } }, "put": { "summary": "Upsert fixed contract line config", "description": "Creates or updates fixed-plan configuration for a contract line.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." }, "required": true, "description": "Contract line UUID from contract_lines.contract_line_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineFixedConfigBody" } } } }, "responses": { "200": { "description": "Fixed config upserted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-lines/{id}/services": { "get": { "summary": "List contract line services", "description": "Returns all service configurations linked to a contract line.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." }, "required": true, "description": "Contract line UUID from contract_lines.contract_line_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Service configurations returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } }, "post": { "summary": "Add service to contract line", "description": "Adds one service configuration to a contract line; rejects duplicates.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." }, "required": true, "description": "Contract line UUID from contract_lines.contract_line_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineAddServiceBody" } } } }, "responses": { "201": { "description": "Service added to contract line.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or service-level validation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-lines/{id}/services/{serviceId}": { "get": { "summary": "Get contract line service details", "description": "Route inventory advertises item lookup, but current route handler delegates to getContractLineServices and returns the full service list for `{id}`; `{serviceId}` is effectively ignored in the GET path.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true, "x-service-id-ignored-by-get-handler": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." }, "required": true, "description": "Contract line UUID from contract_lines.contract_line_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Service UUID from service_catalog.service_id." }, "required": true, "description": "Service UUID from service_catalog.service_id.", "name": "serviceId", "in": "path" } ], "responses": { "200": { "description": "Current implementation returns full service list for the contract line.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } }, "put": { "summary": "Update contract line service", "description": "Updates service configuration for one `{id}` + `{serviceId}` pair.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." }, "required": true, "description": "Contract line UUID from contract_lines.contract_line_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Service UUID from service_catalog.service_id." }, "required": true, "description": "Service UUID from service_catalog.service_id.", "name": "serviceId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineUpdateServiceBody" } } } }, "responses": { "200": { "description": "Service configuration updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } }, "delete": { "summary": "Remove service from contract line", "description": "Removes one service configuration from a contract line.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true, "x-returns-json-with-204-currently": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." }, "required": true, "description": "Contract line UUID from contract_lines.contract_line_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Service UUID from service_catalog.service_id." }, "required": true, "description": "Service UUID from service_catalog.service_id.", "name": "serviceId", "in": "path" } ], "responses": { "204": { "description": "Service removed." }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or service removal failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-lines/{id}/usage-metrics": { "get": { "summary": "Get contract line usage metrics", "description": "Returns usage and cost metrics for a contract line over a time window.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract line UUID from contract_lines.contract_line_id." }, "required": true, "description": "Contract line UUID from contract_lines.contract_line_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "date-time", "description": "Defaults to now minus 30 days when omitted." }, "required": false, "description": "Defaults to now minus 30 days when omitted.", "name": "period_start", "in": "query" }, { "schema": { "type": "string", "format": "date-time", "description": "Defaults to now when omitted." }, "required": false, "description": "Defaults to now when omitted.", "name": "period_end", "in": "query" } ], "responses": { "200": { "description": "Usage metrics returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or usage query failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-line-templates": { "post": { "summary": "Create contract line template", "description": "Creates a plan template record under plan_templates with optional default services.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true, "x-id-provenance": { "template_id": "plan_templates.template_id (uuidv4)" } }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateContractLineTemplateBody" } } } }, "responses": { "201": { "description": "Template created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Request context missing or template creation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/v1/contract-line-templates/{id}/create-contract-line": { "post": { "summary": "Create contract line from template", "description": "Creates a contract line from a template. The template id is taken from the path `{id}` (it takes precedence over any template_id in the body), and the rest of the body is validated with createPlanFromTemplateSchema. Authenticated and tenant-scoped via withApiKeyRouteAuth.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence + request context requirement in controller", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Template UUID from plan_templates.template_id." }, "required": true, "description": "Template UUID from plan_templates.template_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateContractLineFromTemplateBody" } } } }, "responses": { "201": { "description": "Contract line created from template.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "401": { "description": "x-api-key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } }, "500": { "description": "Template application failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContractLineApiError" } } } } } } }, "/api/documents/download/{fileId}": { "get": { "summary": "Download or export document file", "description": "Downloads a stored document file, or exports the document as PDF or Markdown based on the optional format query parameter. The fileId path value may be either a documents.document_id or an external_files.file_id. The route requires an Auth.js session cookie and runs within the session tenant. The normal download path uses document read permission and document authorization rules; PDF and Markdown export paths perform tenant-scoped document lookup and generation.", "tags": [ "Documents" ], "security": [ { "SessionCookieAuth": [] } ], "extensions": { "x-download-formats": [ "original", "pdf", "markdown" ], "x-api-key-auth-skipped": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Document UUID or file UUID. The download handler resolves document_id first, then file_id." }, "required": true, "description": "Document UUID or file UUID. The download handler resolves document_id first, then file_id.", "name": "fileId", "in": "path" }, { "schema": { "type": "string", "enum": [ "pdf", "markdown", "md" ], "description": "Optional export format. pdf generates a PDF; markdown or md exports markdown; omitted downloads the original stored file." }, "required": false, "description": "Optional export format. pdf generates a PDF; markdown or md exports markdown; omitted downloads the original stored file.", "name": "format", "in": "query" } ], "responses": { "200": { "description": "File bytes for the original stored file, generated PDF, or Markdown text. Content-Type varies by requested format and stored MIME type.", "content": { "application/octet-stream": { "schema": { "$ref": "#/components/schemas/BinaryFileResponse" } } } }, "400": { "description": "Invalid or missing fileId.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentDownloadError" } } } }, "401": { "description": "No valid session is present.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentDownloadError" } } } }, "404": { "description": "Document or file was not found, has no associated file, or has no exportable content.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentDownloadError" } } } }, "500": { "description": "Export, generated-PDF download, storage, or unexpected internal failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentDownloadError" } } } } } } }, "/api/documents/{documentId}/download": { "get": { "summary": "Download document attachment", "description": "Downloads the original stored file for a document as an attachment. The documentId path value is resolved against documents.document_id first, then documents.file_id. The route supports Auth.js session-cookie authentication and x-api-key fallback for machine clients, derives the tenant from the authenticated principal, requires document:read permission, and runs document authorization rules before reading the file from storage.", "tags": [ "Documents" ], "security": [ { "SessionCookieAuth": [] }, { "ApiKeyAuth": [] } ], "extensions": { "x-auth-modes": [ "session-cookie", "api-key" ], "x-id-resolution": "document_id_then_file_id", "x-rbac-required": "document:read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Document UUID from documents.document_id, or file UUID from documents.file_id. The handler resolves document_id first and falls back to file_id." }, "required": true, "description": "Document UUID from documents.document_id, or file UUID from documents.file_id. The handler resolves document_id first and falls back to file_id.", "name": "documentId", "in": "path" }, { "schema": { "type": "string", "description": "Optional API key for machine-to-machine download requests. Used only when no valid Auth.js session cookie is present." }, "required": false, "description": "Optional API key for machine-to-machine download requests. Used only when no valid Auth.js session cookie is present.", "name": "x-api-key", "in": "header" } ], "responses": { "200": { "description": "Binary file stream with attachment Content-Disposition headers. Content-Type varies by stored MIME type.", "content": { "application/octet-stream": { "schema": { "$ref": "#/components/schemas/BinaryFileResponse" } } } }, "400": { "description": "Missing documentId path parameter.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentDownloadError" } } } }, "401": { "description": "No valid session or API key was supplied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentDownloadError" } } } }, "403": { "description": "Authenticated user lacks document:read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentDownloadError" } } } }, "404": { "description": "Document not found, document has no file_id, or file metadata/storage record was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentDownloadError" } } } }, "500": { "description": "Unexpected internal failure while downloading the document.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentDownloadError" } } } } } } }, "/api/documents/view/{fileId}": { "get": { "summary": "View document file inline", "description": "Streams a stored file for inline browser viewing, such as images, videos, PDFs, and SVGs. Tenant logo files are public. Other files require either a valid Auth.js session cookie or an x-api-key header, then document-level authorization is checked using RBAC, relationship rules, bundle narrowing, and client visibility rules. Video files support HTTP Range requests and may return 206 Partial Content.", "tags": [ "Documents" ], "security": [ { "SessionCookieAuth": [] }, { "ApiKeyAuth": [] }, {} ], "extensions": { "x-supports-range-requests": true, "x-public-when-tenant-logo": true, "x-api-key-auth-skipped": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "File UUID from external_files.file_id identifying the stored file to serve." }, "required": true, "description": "File UUID from external_files.file_id identifying the stored file to serve.", "name": "fileId", "in": "path" }, { "schema": { "type": "string", "description": "HTTP Range header for partial video responses, for example bytes=0-1048575. Only honored for video/* files." }, "required": false, "description": "HTTP Range header for partial video responses, for example bytes=0-1048575. Only honored for video/* files.", "name": "range", "in": "header" }, { "schema": { "type": "string", "description": "Optional API key fallback for non-browser clients. Used only when no valid session cookie is present." }, "required": false, "description": "Optional API key fallback for non-browser clients. Used only when no valid session cookie is present.", "name": "x-api-key", "in": "header" } ], "responses": { "200": { "description": "Full file stream for inline viewing.", "content": { "application/octet-stream": { "schema": { "$ref": "#/components/schemas/BinaryFileResponse" } } } }, "206": { "description": "Partial file stream for a valid video Range request.", "content": { "application/octet-stream": { "schema": { "$ref": "#/components/schemas/BinaryFileResponse" } } } }, "400": { "description": "Missing fileId or unsupported file type for inline viewing.", "content": { "text/plain": { "schema": { "$ref": "#/components/schemas/DocumentPlainTextError" } } } }, "401": { "description": "No valid session or API key was supplied.", "content": { "text/plain": { "schema": { "$ref": "#/components/schemas/DocumentPlainTextError" } } } }, "403": { "description": "Authenticated user is not authorized to view the file.", "content": { "text/plain": { "schema": { "$ref": "#/components/schemas/DocumentPlainTextError" } } } }, "404": { "description": "File was not found in metadata or storage.", "content": { "text/plain": { "schema": { "$ref": "#/components/schemas/DocumentPlainTextError" } } } }, "416": { "description": "Range header is invalid or outside the file size.", "content": { "text/plain": { "schema": { "$ref": "#/components/schemas/DocumentPlainTextError" } } } }, "500": { "description": "Unexpected internal error while authorizing or streaming the file.", "content": { "text/plain": { "schema": { "$ref": "#/components/schemas/DocumentPlainTextError" } } } } } } }, "/api/email/oauth/initiate": { "post": { "summary": "Initiate email OAuth flow", "description": "Starts the OAuth 2.0 authorization flow for a Google or Microsoft email provider. Requires a valid Auth.js session cookie. The handler builds secure OAuth state containing tenant, user, providerId, redirect URI, timestamp, and nonce, resolves the provider client ID from configured secrets, and returns the authorization URL for the browser to visit.", "tags": [ "Email" ], "security": [ { "SessionCookieAuth": [] } ], "extensions": { "x-api-key-auth-skipped": true }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "description": "Email OAuth initiation payload.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailOAuthInitiateRequest" }, "description": "Email OAuth initiation payload." } } }, "responses": { "200": { "description": "OAuth authorization URL generated successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailOAuthInitiateResponse" } } } }, "400": { "description": "Provider is missing or not supported.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailErrorResponse" } } } }, "401": { "description": "No authenticated user session was found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailErrorResponse" } } } }, "500": { "description": "OAuth client ID was not configured or initiation failed unexpectedly.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailErrorResponse" } } } } } } }, "/api/email/refresh-watch": { "post": { "summary": "Refresh Gmail watch registration", "description": "Forces Pub/Sub setup and Gmail watch registration refresh for an active Google email provider. Requires a valid Auth.js session. The providerId must reference email_providers.id for a provider_type=google record with a google_email_provider_config row and project_id. Internally this calls configureGmailProvider with force=true.", "tags": [ "Email" ], "security": [ { "SessionCookieAuth": [] } ], "x-alga-products": [ "psa" ], "requestBody": { "description": "Gmail provider refresh request.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailRefreshWatchRequest" }, "description": "Gmail provider refresh request." } } }, "responses": { "200": { "description": "Gmail provider Pub/Sub and watch registration refreshed successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailRefreshWatchResponse" } } } }, "400": { "description": "providerId is missing, or the Gmail provider has no project_id configured.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailErrorResponse" } } } }, "401": { "description": "No authenticated user session was found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailErrorResponse" } } } }, "404": { "description": "Gmail provider or Gmail configuration row was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailErrorResponse" } } } }, "500": { "description": "Refresh failed unexpectedly. Response includes success=false and an error message.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailErrorResponse" } } } } } } }, "/api/email/webhooks/google": { "options": { "summary": "CORS preflight for Google email webhook", "description": "CORS preflight response for the Gmail Pub/Sub webhook endpoint. This is handled by global middleware before route authentication or business logic. It does not require a session, API key, tenant, JWT, request body, or query parameter, and always returns 204 No Content with CORS headers.", "tags": [ "Email" ], "security": [], "extensions": { "x-cors-preflight": true, "x-handled-by": "middleware" }, "x-alga-products": [ "psa" ], "responses": { "204": { "description": "CORS preflight accepted; response body is empty." } } }, "post": { "summary": "Receive Google Gmail Pub/Sub webhook", "description": "Receives Google Pub/Sub push notifications for Gmail watches. Standard session and API-key middleware are bypassed; the handler requires an Authorization: Bearer Google-signed JWT, decodes the Pub/Sub message data into a Gmail notification with emailAddress and historyId, resolves the tenant and email provider by subscription name or mailbox, validates Google configuration, and enqueues a pointer-only job into the unified inbound email queue. It does not fetch or return email content.", "tags": [ "Email" ], "security": [ { "GooglePubSubJWT": [] } ], "extensions": { "x-api-key-auth-skipped": true, "x-session-auth-skipped": true, "x-webhook-provider": "google-pubsub" }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Google Pub/Sub push message containing a base64-encoded Gmail notification.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GooglePubSubPushBody" }, "description": "Google Pub/Sub push message containing a base64-encoded Gmail notification." } } }, "responses": { "200": { "description": "Webhook accepted. Either a queue job was enqueued, or the notification was safely skipped.", "content": { "application/json": { "schema": { "anyOf": [ { "$ref": "#/components/schemas/GoogleWebhookEnqueueResponse" }, { "$ref": "#/components/schemas/GoogleWebhookSkippedResponse" } ] } } } }, "400": { "description": "Permanent parse or validation error; Pub/Sub should not retry.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailErrorResponse" } } } }, "401": { "description": "Bearer JWT is missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailErrorResponse" } } } }, "503": { "description": "Transient enqueue or processing failure; Pub/Sub may retry.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailErrorResponse" } } } } } } }, "/api/email/webhooks/microsoft": { "get": { "summary": "Validate Microsoft email webhook subscription", "description": "Microsoft Graph subscription validation endpoint. During subscription creation or renewal, Microsoft calls this endpoint with validationtoken or validationToken. The handler echoes the token verbatim as text/plain. If no token is provided, it returns OK. No session, API key, tenant header, or request body is required.", "tags": [ "Email" ], "security": [], "extensions": { "x-api-key-auth-skipped": true, "x-webhook-provider": "microsoft-graph" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Microsoft Graph subscription validation token. The handler also accepts validationToken with camel-case spelling." }, "required": false, "description": "Microsoft Graph subscription validation token. The handler also accepts validationToken with camel-case spelling.", "name": "validationtoken", "in": "query" }, { "schema": { "type": "string", "description": "Camel-case variant of the Microsoft Graph validation token." }, "required": false, "description": "Camel-case variant of the Microsoft Graph validation token.", "name": "validationToken", "in": "query" } ], "responses": { "200": { "description": "Plain text validation token echo, or OK when no validation token is present.", "content": { "text/plain": { "schema": { "$ref": "#/components/schemas/MicrosoftWebhookTextResponse" } } } }, "500": { "description": "Unexpected error while handling validation request.", "content": { "text/plain": { "schema": { "$ref": "#/components/schemas/MicrosoftWebhookTextResponse" } } } } } }, "options": { "summary": "CORS preflight for Microsoft email webhook", "description": "CORS preflight response for the Microsoft Graph email webhook endpoint. Global middleware intercepts OPTIONS before route logic and returns 204 No Content with CORS headers. No authentication, tenant, request body, query parameter, or database access is required.", "tags": [ "Email" ], "security": [], "extensions": { "x-cors-preflight": true, "x-handled-by": "middleware" }, "x-alga-products": [ "psa" ], "responses": { "204": { "description": "CORS preflight accepted; response body is empty." } } }, "post": { "summary": "Receive Microsoft Graph email webhook", "description": "Receives Microsoft Graph change notifications for monitored mailboxes. Standard session and API-key middleware are bypassed. The handler supports validation token echo, parses Microsoft notification batches, resolves provider and tenant by matching notification subscriptionId to microsoft_email_provider_config.webhook_subscription_id, validates clientState against the stored webhook_verification_token when configured, extracts message IDs, and enqueues pointer-only jobs into the unified inbound email queue. The tenantId in the Microsoft payload is informational and is not trusted for tenant resolution.", "tags": [ "Email" ], "security": [], "extensions": { "x-api-key-auth-skipped": true, "x-session-auth-skipped": true, "x-webhook-provider": "microsoft-graph" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Microsoft Graph subscription validation token. The handler also accepts validationToken with camel-case spelling." }, "required": false, "description": "Microsoft Graph subscription validation token. The handler also accepts validationToken with camel-case spelling.", "name": "validationtoken", "in": "query" }, { "schema": { "type": "string", "description": "Camel-case variant of the Microsoft Graph validation token." }, "required": false, "description": "Camel-case variant of the Microsoft Graph validation token.", "name": "validationToken", "in": "query" } ], "requestBody": { "description": "Microsoft Graph webhook notification batch.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MicrosoftGraphWebhookBody" }, "description": "Microsoft Graph webhook notification batch." } } }, "responses": { "200": { "description": "Validation token echoed as text/plain, or JSON success response after notifications are processed or skipped.", "content": { "application/json": { "schema": { "anyOf": [ { "$ref": "#/components/schemas/MicrosoftWebhookSuccessResponse" }, { "$ref": "#/components/schemas/MicrosoftWebhookEmptyResponse" }, { "$ref": "#/components/schemas/MicrosoftWebhookTextResponse" } ] } } } }, "500": { "description": "Unexpected internal server error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailErrorResponse" } } } }, "503": { "description": "One or more notification pointers could not be enqueued.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MicrosoftWebhookEnqueueErrorResponse" } } } } } } }, "/api/email/webhooks/test": { "post": { "summary": "Publish test inbound email event", "description": "Publishes a synthetic INBOUND_EMAIL_RECEIVED event to the workflow event stream so operators can test webhook and workflow delivery without a real provider notification. Requires a valid Auth.js session; tenant is taken from the authenticated user session. The route performs no RBAC check and does not require an API key.", "tags": [ "Email" ], "security": [ { "SessionCookieAuth": [] } ], "x-alga-products": [ "psa" ], "requestBody": { "description": "Optional synthetic email provider metadata for the test event.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailWebhookTestRequest" }, "description": "Optional synthetic email provider metadata for the test event." } } }, "responses": { "200": { "description": "Test event was published successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailWebhookTestResponse" } } } }, "401": { "description": "No authenticated user session was found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailErrorResponse" } } } }, "500": { "description": "Failed to publish the test event.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailErrorResponse" } } } } } } }, "/api/ext/{extensionId}/{path}": { "get": { "summary": "Forward GET request to extension runner", "description": "Tenant-scoped extension gateway endpoint that forwards GET requests to an installed extension runner. The gateway resolves the tenant from x-alga-tenant, x-tenant-id, session cookie, or DEV_TENANT_ID in development; verifies the extension is installed and enabled for that tenant; forwards selected headers and all query parameters to RUNNER_BASE_URL /v1/execute; and relays the runner response. GET requests do not read a body and do not generate an idempotency key. The gateway currently has a placeholder access check and does not enforce per-extension RBAC beyond tenant install resolution.", "tags": [ "Extension Gateway" ], "security": [ { "SessionCookieAuth": [] } ], "extensions": { "x-api-key-auth-skipped": true, "x-proxy-target": "extension-runner", "x-request-body-read": false, "x-idempotency-generated": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "minLength": 1, "description": "Extension identifier from the URL. This may be a registry/install UUID or a publisher.name slug, resolved case-insensitively." }, "required": true, "description": "Extension identifier from the URL. This may be a registry/install UUID or a publisher.name slug, resolved case-insensitively.", "name": "extensionId", "in": "path" }, { "schema": { "type": "string", "description": "Remaining extension endpoint path after extensionId. The gateway forwards this path to the runner unchanged." }, "required": false, "description": "Remaining extension endpoint path after extensionId. The gateway forwards this path to the runner unchanged.", "name": "path", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Optional request ID. The gateway generates a UUID when absent and forwards it to the runner." }, "required": false, "description": "Optional request ID. The gateway generates a UUID when absent and forwards it to the runner.", "name": "x-request-id", "in": "header" }, { "schema": { "type": "string", "description": "Optional idempotency key for non-GET methods. The gateway falls back to x-request-id when absent and forwards the key to the runner." }, "required": false, "description": "Optional idempotency key for non-GET methods. The gateway falls back to x-request-id when absent and forwards the key to the runner.", "name": "x-idempotency-key", "in": "header" }, { "schema": { "type": "string", "description": "Internal tenant header used for tenant resolution before session fallback." }, "required": false, "description": "Internal tenant header used for tenant resolution before session fallback.", "name": "x-alga-tenant", "in": "header" }, { "schema": { "type": "string", "description": "Legacy tenant header accepted for tenant resolution before session fallback." }, "required": false, "description": "Legacy tenant header accepted for tenant resolution before session fallback.", "name": "x-tenant-id", "in": "header" } ], "responses": { "200": { "description": "Runner response relayed by the gateway. The actual status can vary by extension contract.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionOpaqueResponse" } } } }, "204": { "description": "Runner returned no content, or CORS preflight for OPTIONS requests on this gateway path." }, "404": { "description": "Extension is not installed or not enabled for the resolved tenant.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } }, "500": { "description": "Tenant could not be resolved or another internal gateway error occurred.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } }, "502": { "description": "Runner call failed, runner response was empty/invalid, or install context was incomplete.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } } } }, "post": { "summary": "Forward POST request to extension runner", "description": "Tenant-scoped extension gateway endpoint that forwards POST requests to an installed extension runner. The gateway resolves the tenant from x-alga-tenant, x-tenant-id, session cookie, or DEV_TENANT_ID in development; verifies the extension is installed and enabled for that tenant; forwards selected headers, query parameters, and an optional opaque body to RUNNER_BASE_URL /v1/execute; and relays the runner response. For POST requests the body is limited to 10 MB, base64-encoded, and forwarded as http.body_b64. An x-idempotency-key header is forwarded when supplied, otherwise the generated x-request-id is used as the non-GET idempotency fallback. The gateway currently has a placeholder access check and does not enforce per-extension RBAC beyond tenant install resolution.", "tags": [ "Extension Gateway" ], "security": [ { "SessionCookieAuth": [] } ], "extensions": { "x-api-key-auth-skipped": true, "x-proxy-target": "extension-runner", "x-max-body-bytes": 10485760, "x-idempotency-supported": true, "x-idempotency-key-header": "x-idempotency-key", "x-idempotency-fallback": "x-request-id" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "minLength": 1, "description": "Extension identifier from the URL. This may be a registry/install UUID or a publisher.name slug, resolved case-insensitively." }, "required": true, "description": "Extension identifier from the URL. This may be a registry/install UUID or a publisher.name slug, resolved case-insensitively.", "name": "extensionId", "in": "path" }, { "schema": { "type": "string", "description": "Remaining extension endpoint path after extensionId. The gateway forwards this path to the runner unchanged." }, "required": false, "description": "Remaining extension endpoint path after extensionId. The gateway forwards this path to the runner unchanged.", "name": "path", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Optional request ID. The gateway generates a UUID when absent and forwards it to the runner." }, "required": false, "description": "Optional request ID. The gateway generates a UUID when absent and forwards it to the runner.", "name": "x-request-id", "in": "header" }, { "schema": { "type": "string", "description": "Optional idempotency key for non-GET methods. The gateway falls back to x-request-id when absent and forwards the key to the runner." }, "required": false, "description": "Optional idempotency key for non-GET methods. The gateway falls back to x-request-id when absent and forwards the key to the runner.", "name": "x-idempotency-key", "in": "header" }, { "schema": { "type": "string", "description": "Internal tenant header used for tenant resolution before session fallback." }, "required": false, "description": "Internal tenant header used for tenant resolution before session fallback.", "name": "x-alga-tenant", "in": "header" }, { "schema": { "type": "string", "description": "Legacy tenant header accepted for tenant resolution before session fallback." }, "required": false, "description": "Legacy tenant header accepted for tenant resolution before session fallback.", "name": "x-tenant-id", "in": "header" } ], "requestBody": { "description": "Optional extension-specific POST body. The gateway accepts any content type up to 10 MB and forwards it as base64.", "required": false, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionOpaqueRequest" }, "description": "Optional extension-specific POST body. The gateway accepts any content type up to 10 MB and forwards it as base64." } } }, "responses": { "200": { "description": "Runner response relayed by the gateway. The actual status can vary by extension contract.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionOpaqueResponse" } } } }, "204": { "description": "Runner returned no content, or CORS preflight for OPTIONS requests on this gateway path." }, "404": { "description": "Extension is not installed or not enabled for the resolved tenant.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } }, "413": { "description": "Request body exceeds the 10 MB gateway limit.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } }, "500": { "description": "Tenant could not be resolved or another internal gateway error occurred.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } }, "502": { "description": "Runner call failed, runner response was empty/invalid, or install context was incomplete.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } } } }, "put": { "summary": "Forward PUT request to extension runner", "description": "Tenant-scoped extension gateway endpoint that forwards PUT requests to an installed extension runner. The gateway resolves the tenant from x-alga-tenant, x-tenant-id, session cookie, or DEV_TENANT_ID in development; verifies the extension is installed and enabled for that tenant; forwards selected headers, query parameters, and an optional opaque body to RUNNER_BASE_URL /v1/execute; and relays the runner response. For PUT requests the body is limited to 10 MB, base64-encoded, and forwarded as http.body_b64. Clients should provide x-idempotency-key for safe retries; otherwise the gateway falls back to a generated request ID. The gateway currently has a placeholder access check and does not enforce per-extension RBAC beyond tenant install resolution.", "tags": [ "Extension Gateway" ], "security": [ { "SessionCookieAuth": [] } ], "extensions": { "x-api-key-auth-skipped": true, "x-proxy-target": "extension-runner", "x-max-body-bytes": 10485760, "x-idempotency-supported": true, "x-idempotency-key-header": "x-idempotency-key", "x-idempotency-fallback": "x-request-id" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "minLength": 1, "description": "Extension identifier from the URL. This may be a registry/install UUID or a publisher.name slug, resolved case-insensitively." }, "required": true, "description": "Extension identifier from the URL. This may be a registry/install UUID or a publisher.name slug, resolved case-insensitively.", "name": "extensionId", "in": "path" }, { "schema": { "type": "string", "description": "Remaining extension endpoint path after extensionId. The gateway forwards this path to the runner unchanged." }, "required": false, "description": "Remaining extension endpoint path after extensionId. The gateway forwards this path to the runner unchanged.", "name": "path", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Optional request ID. The gateway generates a UUID when absent and forwards it to the runner." }, "required": false, "description": "Optional request ID. The gateway generates a UUID when absent and forwards it to the runner.", "name": "x-request-id", "in": "header" }, { "schema": { "type": "string", "description": "Optional idempotency key for non-GET methods. The gateway falls back to x-request-id when absent and forwards the key to the runner." }, "required": false, "description": "Optional idempotency key for non-GET methods. The gateway falls back to x-request-id when absent and forwards the key to the runner.", "name": "x-idempotency-key", "in": "header" }, { "schema": { "type": "string", "description": "Internal tenant header used for tenant resolution before session fallback." }, "required": false, "description": "Internal tenant header used for tenant resolution before session fallback.", "name": "x-alga-tenant", "in": "header" }, { "schema": { "type": "string", "description": "Legacy tenant header accepted for tenant resolution before session fallback." }, "required": false, "description": "Legacy tenant header accepted for tenant resolution before session fallback.", "name": "x-tenant-id", "in": "header" } ], "requestBody": { "description": "Optional extension-specific PUT body. The gateway accepts any content type up to 10 MB and forwards it as base64.", "required": false, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionOpaqueRequest" }, "description": "Optional extension-specific PUT body. The gateway accepts any content type up to 10 MB and forwards it as base64." } } }, "responses": { "200": { "description": "Runner response relayed by the gateway. The actual status can vary by extension contract.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionOpaqueResponse" } } } }, "204": { "description": "Runner returned no content, or CORS preflight for OPTIONS requests on this gateway path." }, "404": { "description": "Extension is not installed or not enabled for the resolved tenant.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } }, "413": { "description": "Request body exceeds the 10 MB gateway limit.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } }, "500": { "description": "Tenant could not be resolved or another internal gateway error occurred.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } }, "502": { "description": "Runner call failed, runner response was empty/invalid, or install context was incomplete.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } } } }, "patch": { "summary": "Forward PATCH request to extension runner", "description": "Tenant-scoped extension gateway endpoint that forwards PATCH requests to an installed extension runner. The gateway resolves the tenant from x-alga-tenant, x-tenant-id, session cookie, or DEV_TENANT_ID in development; verifies the extension is installed and enabled for that tenant; forwards selected headers, query parameters, and an optional opaque body to RUNNER_BASE_URL /v1/execute; and relays the runner response. For PATCH requests the body is limited to 10 MB, base64-encoded, and forwarded as http.body_b64. Clients should provide x-idempotency-key for safe retries; otherwise the gateway falls back to a generated request ID. The gateway does not interpret PATCH semantics; partial-update behavior is extension-defined.", "tags": [ "Extension Gateway" ], "security": [ { "SessionCookieAuth": [] } ], "extensions": { "x-api-key-auth-skipped": true, "x-proxy-target": "extension-runner", "x-max-body-bytes": 10485760, "x-idempotency-supported": true, "x-idempotency-key-header": "x-idempotency-key", "x-idempotency-fallback": "x-request-id" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "minLength": 1, "description": "Extension identifier from the URL. This may be a registry/install UUID or a publisher.name slug, resolved case-insensitively." }, "required": true, "description": "Extension identifier from the URL. This may be a registry/install UUID or a publisher.name slug, resolved case-insensitively.", "name": "extensionId", "in": "path" }, { "schema": { "type": "string", "description": "Remaining extension endpoint path after extensionId. The gateway forwards this path to the runner unchanged." }, "required": false, "description": "Remaining extension endpoint path after extensionId. The gateway forwards this path to the runner unchanged.", "name": "path", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Optional request ID. The gateway generates a UUID when absent and forwards it to the runner." }, "required": false, "description": "Optional request ID. The gateway generates a UUID when absent and forwards it to the runner.", "name": "x-request-id", "in": "header" }, { "schema": { "type": "string", "description": "Optional idempotency key for non-GET methods. The gateway falls back to x-request-id when absent and forwards the key to the runner." }, "required": false, "description": "Optional idempotency key for non-GET methods. The gateway falls back to x-request-id when absent and forwards the key to the runner.", "name": "x-idempotency-key", "in": "header" }, { "schema": { "type": "string", "description": "Internal tenant header used for tenant resolution before session fallback." }, "required": false, "description": "Internal tenant header used for tenant resolution before session fallback.", "name": "x-alga-tenant", "in": "header" }, { "schema": { "type": "string", "description": "Legacy tenant header accepted for tenant resolution before session fallback." }, "required": false, "description": "Legacy tenant header accepted for tenant resolution before session fallback.", "name": "x-tenant-id", "in": "header" } ], "requestBody": { "description": "Optional extension-specific PATCH body. The gateway accepts any content type up to 10 MB and forwards it as base64.", "required": false, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionOpaqueRequest" }, "description": "Optional extension-specific PATCH body. The gateway accepts any content type up to 10 MB and forwards it as base64." } } }, "responses": { "200": { "description": "Runner response relayed by the gateway. The actual status can vary by extension contract.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionOpaqueResponse" } } } }, "204": { "description": "Runner returned no content, or CORS preflight for OPTIONS requests on this gateway path." }, "404": { "description": "Extension is not installed or not enabled for the resolved tenant.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } }, "413": { "description": "Request body exceeds the 10 MB gateway limit.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } }, "500": { "description": "Tenant could not be resolved or another internal gateway error occurred.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } }, "502": { "description": "Runner call failed, runner response was empty/invalid, or install context was incomplete.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } } } }, "delete": { "summary": "Forward DELETE request to extension runner", "description": "Tenant-scoped extension gateway endpoint that forwards DELETE requests to an installed extension runner. The gateway resolves the tenant from x-alga-tenant, x-tenant-id, session cookie, or DEV_TENANT_ID in development; verifies the extension is installed and enabled for that tenant; forwards selected headers, query parameters, and an optional opaque body to RUNNER_BASE_URL /v1/execute; and relays the runner response. For DELETE requests the body, if present, is limited to 10 MB, base64-encoded, and forwarded as http.body_b64. The gateway currently has a placeholder access check and does not enforce per-extension RBAC beyond tenant install resolution.", "tags": [ "Extension Gateway" ], "security": [ { "SessionCookieAuth": [] } ], "extensions": { "x-api-key-auth-skipped": true, "x-proxy-target": "extension-runner", "x-max-body-bytes": 10485760, "x-idempotency-supported": true, "x-idempotency-key-header": "x-idempotency-key", "x-idempotency-fallback": "x-request-id" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "minLength": 1, "description": "Extension identifier from the URL. This may be a registry/install UUID or a publisher.name slug, resolved case-insensitively." }, "required": true, "description": "Extension identifier from the URL. This may be a registry/install UUID or a publisher.name slug, resolved case-insensitively.", "name": "extensionId", "in": "path" }, { "schema": { "type": "string", "description": "Remaining extension endpoint path after extensionId. The gateway forwards this path to the runner unchanged." }, "required": false, "description": "Remaining extension endpoint path after extensionId. The gateway forwards this path to the runner unchanged.", "name": "path", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Optional request ID. The gateway generates a UUID when absent and forwards it to the runner." }, "required": false, "description": "Optional request ID. The gateway generates a UUID when absent and forwards it to the runner.", "name": "x-request-id", "in": "header" }, { "schema": { "type": "string", "description": "Optional idempotency key for non-GET methods. The gateway falls back to x-request-id when absent and forwards the key to the runner." }, "required": false, "description": "Optional idempotency key for non-GET methods. The gateway falls back to x-request-id when absent and forwards the key to the runner.", "name": "x-idempotency-key", "in": "header" }, { "schema": { "type": "string", "description": "Internal tenant header used for tenant resolution before session fallback." }, "required": false, "description": "Internal tenant header used for tenant resolution before session fallback.", "name": "x-alga-tenant", "in": "header" }, { "schema": { "type": "string", "description": "Legacy tenant header accepted for tenant resolution before session fallback." }, "required": false, "description": "Legacy tenant header accepted for tenant resolution before session fallback.", "name": "x-tenant-id", "in": "header" } ], "requestBody": { "description": "Optional extension-specific DELETE body. The gateway accepts any content type up to 10 MB and forwards it as base64.", "required": false, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionOpaqueRequest" }, "description": "Optional extension-specific DELETE body. The gateway accepts any content type up to 10 MB and forwards it as base64." } } }, "responses": { "200": { "description": "Runner response relayed by the gateway. The actual status can vary by extension contract.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionOpaqueResponse" } } } }, "204": { "description": "Runner returned no content, or CORS preflight for OPTIONS requests on this gateway path." }, "404": { "description": "Extension is not installed or not enabled for the resolved tenant.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } }, "413": { "description": "Request body exceeds the 10 MB gateway limit.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } }, "500": { "description": "Tenant could not be resolved or another internal gateway error occurred.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } }, "502": { "description": "Runner call failed, runner response was empty/invalid, or install context was incomplete.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionGatewayErrorResponse" } } } } } }, "options": { "summary": "OPTIONS ext", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "ext" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/billing-analytics/overview": { "get": { "summary": "Get billing overview analytics", "description": "Maps to ApiContractLineController.getBillingOverviewAnalytics() and returns tenant billing overview analytics. Authenticated and tenant-scoped via withApiKeyRouteAuth (req.context is populated before the handler runs).", "tags": [ "Billing Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-source": "middleware API-key presence check only", "x-request-context-required": true, "x-request-context-wiring-gap": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Billing analytics payload returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillingOverviewResponse" } } } }, "500": { "description": "Request context missing or other server failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/billing/calculate": { "post": { "summary": "Calculate client billing charges", "description": "Calculates billing for one client and billing window using FinancialService.calculateBilling(). Requires financial:read permission.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialCalculateBillingBody" } } } }, "responses": { "200": { "description": "Billing calculation returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid billing calculation payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected billing calculation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/billing/payment-terms": { "get": { "summary": "List billing payment terms", "description": "Returns payment terms from FinancialService.getPaymentTerms().", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Payment terms returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected payment terms failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/bulk/invoices": { "post": { "summary": "Run bulk invoice operation", "description": "FinancialService.bulkInvoiceOperation() supports send/finalize/cancel/mark_paid operations.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "update" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialBulkInvoiceOperationBody" } } } }, "responses": { "200": { "description": "Bulk operation processed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid bulk operation payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:update permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected bulk operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/bulk/transactions": { "post": { "summary": "Run bulk transaction operation", "description": "FinancialService.bulkTransactionOperation() applies approve/reject/reverse to existing transactions (1–100 ids). reverse posts a compensating transaction linked to the original via related_transaction_id, recomputes the running balance, and syncs the client credit balance for credit-type transactions. Returns per-id results { total_requested, successful, failed, results[] }.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "update" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialBulkTransactionOperationBody" } } } }, "responses": { "200": { "description": "Bulk operation processed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid bulk operation payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:update permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected bulk operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/bulk/credits": { "post": { "summary": "Run bulk credit operation", "description": "FinancialService.bulkCreditOperation() applies expire/extend_expiration/transfer to existing credits (1–100 ids). expire forfeits the remaining amount via a credit_expiration transaction and reduces the client balance; extend_expiration sets a new expiration_date; transfer moves the remaining amount to parameters.target_client_id. Returns per-id results { total_requested, successful, failed, results[] }.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "update" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialBulkCreditOperationBody" } } } }, "responses": { "200": { "description": "Bulk operation processed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid bulk operation payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:update permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected bulk operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/credits": { "get": { "summary": "List credit balances and records", "description": "Lists credits with filters using FinancialService.listClientCredits().", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_to", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_to", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "client_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "invoice_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "type", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_min", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_max", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_expired", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "expiring_soon", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_remaining", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_expiration", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_to", "in": "query" }, { "schema": { "type": "string", "enum": [ "day", "week", "month" ] }, "required": false, "name": "group_by", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_projections", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "as_of_date", "in": "query" } ], "responses": { "200": { "description": "Paginated credit records returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiPaginated" } } } }, "400": { "description": "Invalid credit query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected credit listing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/credits/apply": { "post": { "summary": "Apply credit to invoice", "description": "Applies client credit to an invoice via FinancialService.applyCreditToInvoice().", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "update" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialApplyCreditToInvoiceBody" } } } }, "responses": { "200": { "description": "Credit operation completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "201": { "description": "Credit operation created a new record.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:update permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected credit operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/credits/prepayment": { "post": { "summary": "Create prepayment invoice", "description": "Creates a prepayment invoice and corresponding credit allocation.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "create" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialPrepaymentInvoiceBody" } } } }, "responses": { "200": { "description": "Credit operation completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "201": { "description": "Credit operation created a new record.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:create permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected credit operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/credits/transfer": { "post": { "summary": "Transfer credit between clients", "description": "Transfers credit between clients via FinancialService.transferCredit().", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "transfer" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialTransferCreditBody" } } } }, "responses": { "200": { "description": "Credit operation completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "201": { "description": "Credit operation created a new record.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:transfer permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected credit operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/credits/validate": { "post": { "summary": "Validate client credit balance", "description": "Validates whether a client has sufficient usable credit.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialValidateCreditBody" } } } }, "responses": { "200": { "description": "Credit operation completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "201": { "description": "Credit operation created a new record.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected credit operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/invoices": { "get": { "summary": "List financial invoices (transaction list wiring)", "description": "Route file maps to ApiFinancialController.list(), which is transaction-oriented (resource financial/transactions) rather than invoice-specific list logic. Current response is the generic transaction list envelope with financial report links.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "read", "x-route-to-controller-mismatch": true, "x-controller-method": "ApiFinancialController.list()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_to", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_to", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "client_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "invoice_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "type", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_min", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_max", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_expired", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "expiring_soon", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_remaining", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_expiration", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_to", "in": "query" }, { "schema": { "type": "string", "enum": [ "day", "week", "month" ] }, "required": false, "name": "group_by", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_projections", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "as_of_date", "in": "query" } ], "responses": { "200": { "description": "Paginated financial list returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiPaginated" } } } }, "400": { "description": "Invalid query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected listing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/invoices/{id}/finalize": { "post": { "summary": "Finalize financial invoice (maps to generic update)", "description": "Despite route naming, the handler calls ApiFinancialController.update() which performs generic financial update validation and update semantics. Path id is extracted as financial resource id and processed by FinancialService.update().", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "update", "x-route-to-controller-mismatch": true, "x-controller-method": "ApiFinancialController.update()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialUpdateTransactionBody" } } } }, "responses": { "200": { "description": "Financial resource updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid id or payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:update permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Financial resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected update failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/invoices/{id}/items": { "post": { "summary": "Add financial invoice item (maps to generic create)", "description": "Route maps to ApiFinancialController.create(), which validates createTransactionSchema and creates a financial transaction. The path {id} is not consumed by create().", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "create", "x-route-to-controller-mismatch": true, "x-controller-method": "ApiFinancialController.create()", "x-path-param-currently-unused": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialCreateTransactionBody" } } } }, "responses": { "201": { "description": "Financial transaction created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:create permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected create failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/payment-methods": { "get": { "summary": "List payment methods (transaction list wiring)", "description": "Current route wiring calls ApiFinancialController.list(), which lists transaction records and not payment methods. This discrepancy is documented as implementation behavior.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "read", "x-route-to-controller-mismatch": true, "x-controller-method": "ApiFinancialController.list()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_to", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_to", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "client_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "invoice_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "type", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_min", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_max", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_expired", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "expiring_soon", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_remaining", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_expiration", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_to", "in": "query" }, { "schema": { "type": "string", "enum": [ "day", "week", "month" ] }, "required": false, "name": "group_by", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_projections", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "as_of_date", "in": "query" } ], "responses": { "200": { "description": "Paginated list returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiPaginated" } } } }, "400": { "description": "Invalid query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected list failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } }, "post": { "summary": "Create payment method", "description": "Creates a payment method using FinancialService.createPaymentMethod().", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "create" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialCreatePaymentMethodBody" } } } }, "responses": { "201": { "description": "Payment method created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:create permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected payment method creation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/payment-methods/{id}": { "get": { "summary": "Get payment method by id (transaction get wiring)", "description": "Route maps to ApiFinancialController.getById(), which fetches a financial transaction by id.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "read", "x-route-to-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "204": { "description": "Resource deleted." }, "400": { "description": "Invalid id or request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } }, "put": { "summary": "Update payment method by id (transaction update wiring)", "description": "Route maps to ApiFinancialController.update(), which updates generic financial resource fields.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "update", "x-route-to-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialUpdateTransactionBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "204": { "description": "Resource deleted." }, "400": { "description": "Invalid id or request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:update permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } }, "delete": { "summary": "Delete payment method by id (transaction delete wiring)", "description": "Route maps to ApiFinancialController.delete(), deleting a generic financial resource by id.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "delete", "x-route-to-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "204": { "description": "Resource deleted." }, "400": { "description": "Invalid id or request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:delete permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/reconciliation/run": { "post": { "summary": "Run financial reconciliation", "description": "Triggers FinancialService.runCreditReconciliation(). Optional client_id query narrows reconciliation target.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "update" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_to", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_to", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "client_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "invoice_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "type", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_min", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_max", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_expired", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "expiring_soon", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_remaining", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_expiration", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_to", "in": "query" }, { "schema": { "type": "string", "enum": [ "day", "week", "month" ] }, "required": false, "name": "group_by", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_projections", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "as_of_date", "in": "query" } ], "responses": { "200": { "description": "Reconciliation run completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:update permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected reconciliation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/reconciliation/{id}/resolve": { "post": { "summary": "Resolve reconciliation report", "description": "Resolves one reconciliation report by id with optional operator notes.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "update" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialReconciliationResolveBody" } } } }, "responses": { "200": { "description": "Reconciliation report resolved.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid id or payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:update permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Reconciliation report not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected resolution failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/reports/aging": { "get": { "summary": "Get aging report", "description": "Returns aging buckets and summary for tenant-wide receivables or one client.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_to", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_to", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "client_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "invoice_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "type", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_min", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_max", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_expired", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "expiring_soon", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_remaining", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_expiration", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_to", "in": "query" }, { "schema": { "type": "string", "enum": [ "day", "week", "month" ] }, "required": false, "name": "group_by", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_projections", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "as_of_date", "in": "query" } ], "responses": { "200": { "description": "Report returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid report query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected report failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/reports/analytics": { "get": { "summary": "Get financial analytics", "description": "Returns aggregate financial analytics for a date range.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_to", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_to", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "client_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "invoice_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "type", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_min", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_max", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_expired", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "expiring_soon", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_remaining", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_expiration", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_to", "in": "query" }, { "schema": { "type": "string", "enum": [ "day", "week", "month" ] }, "required": false, "name": "group_by", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_projections", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "as_of_date", "in": "query" } ], "responses": { "200": { "description": "Report returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid report query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected report failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/reports/balance": { "get": { "summary": "Get account balance report", "description": "Returns balance summary for one client_id (required by controller).", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_to", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_to", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "client_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "invoice_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "type", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_min", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_max", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_expired", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "expiring_soon", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_remaining", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_expiration", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_to", "in": "query" }, { "schema": { "type": "string", "enum": [ "day", "week", "month" ] }, "required": false, "name": "group_by", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_projections", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "as_of_date", "in": "query" } ], "responses": { "200": { "description": "Report returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid report query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected report failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/tax/calculate": { "post": { "summary": "Calculate financial tax", "description": "Calculates tax for a client/amount/tax_region tuple. Requires financial:read permission in current controller implementation.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialCalculateTaxBody" } } } }, "responses": { "200": { "description": "Tax calculation returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid tax request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected tax calculation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/tax/rates": { "get": { "summary": "List financial tax rates (transaction list wiring)", "description": "Route file currently maps to ApiFinancialController.list(), so the response is the generic financial transaction list rather than a tax-rate list.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "read", "x-route-to-controller-mismatch": true, "x-controller-method": "ApiFinancialController.list()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_to", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_to", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "client_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "invoice_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "type", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_min", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_max", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_expired", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "expiring_soon", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_remaining", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_expiration", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_to", "in": "query" }, { "schema": { "type": "string", "enum": [ "day", "week", "month" ] }, "required": false, "name": "group_by", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_projections", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "as_of_date", "in": "query" } ], "responses": { "200": { "description": "Paginated list returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiPaginated" } } } }, "400": { "description": "Invalid query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected list failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/transactions": { "get": { "summary": "List financial transactions", "description": "Lists transactions with advanced filtering.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "created_to", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "updated_to", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "client_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "invoice_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "type", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_min", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "amount_max", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_expired", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "expiring_soon", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_remaining", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_expiration", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_to", "in": "query" }, { "schema": { "type": "string", "enum": [ "day", "week", "month" ] }, "required": false, "name": "group_by", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_projections", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "as_of_date", "in": "query" } ], "responses": { "200": { "description": "Paginated transactions returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiPaginated" } } } }, "400": { "description": "Invalid transaction query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected transaction listing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } }, "post": { "summary": "Create financial transaction", "description": "Creates one financial transaction.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "create" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialCreateTransactionBody" } } } }, "responses": { "201": { "description": "Transaction created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid transaction payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:create permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected transaction creation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial/transactions/{id}": { "get": { "summary": "Get financial transaction by id", "description": "Loads one financial transaction by id.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Transaction returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid transaction id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Transaction not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected transaction retrieval failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } }, "put": { "summary": "Update financial transaction", "description": "Updates one transaction id with partial payload.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "financial", "x-rbac-action": "update" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialUpdateTransactionBody" } } } }, "responses": { "200": { "description": "Transaction updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid transaction id or payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "financial:update permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Transaction not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected transaction update failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices": { "get": { "summary": "List invoices", "description": "Lists invoices with pagination, filter query keys, and include flags.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_items", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_client", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_billing_cycle", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_transactions", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "q", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "to", "in": "query" }, { "schema": { "type": "string", "enum": [ "json", "csv" ] }, "required": false, "name": "format", "in": "query" } ], "responses": { "200": { "description": "Paginated invoices returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiPaginated" } } } }, "400": { "description": "Invalid list query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected invoice list failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } }, "post": { "summary": "Create invoice", "description": "Creates an invoice record via ApiBaseController.create() and invoice create schema.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "create" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceCreateBody" } } } }, "responses": { "201": { "description": "Invoice created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid invoice payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:create permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected invoice creation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/analytics": { "get": { "summary": "Get invoice analytics", "description": "Returns aggregate analytics for invoice states and amounts over optional from/to range.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "analytics" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_items", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_client", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_billing_cycle", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_transactions", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "q", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "to", "in": "query" }, { "schema": { "type": "string", "enum": [ "json", "csv" ] }, "required": false, "name": "format", "in": "query" } ], "responses": { "200": { "description": "Invoice analytics returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:analytics permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected analytics failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/bulk": { "post": { "summary": "Bulk update invoice status", "description": "Bulk status transition route using bulkInvoiceStatusUpdateSchema.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "bulk_update" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceBulkStatusBody" } } } }, "responses": { "200": { "description": "Bulk status update completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid bulk status payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:bulk_update permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected bulk status failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/bulk/credit": { "post": { "summary": "Bulk apply invoice credits", "description": "Applies the same credit amount to each listed invoice; response includes successes and per-invoice errors.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "bulk_credit" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceBulkCreditBody" } } } }, "responses": { "200": { "description": "Bulk credit operation completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid bulk credit payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:bulk_credit permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected bulk credit failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/bulk/delete": { "post": { "summary": "Bulk delete invoices", "description": "Deletes multiple invoice records in one request.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "bulk_delete" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceBulkDeleteBody" } } } }, "responses": { "200": { "description": "Bulk delete completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid bulk delete payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:bulk_delete permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected bulk delete failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/bulk/send": { "post": { "summary": "Bulk send invoices", "description": "Enqueues or executes invoice send for multiple invoice IDs.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "bulk_send" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceBulkSendBody" } } } }, "responses": { "200": { "description": "Bulk send completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid bulk send payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:bulk_send permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected bulk send failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/export": { "get": { "summary": "Export invoices", "description": "Exports invoice data. format=csv returns text/csv attachment; default is JSON success envelope.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "read", "x-alt-response-content-type": "text/csv" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_items", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_client", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_billing_cycle", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_transactions", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "q", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "to", "in": "query" }, { "schema": { "type": "string", "enum": [ "json", "csv" ] }, "required": false, "name": "format", "in": "query" } ], "responses": { "200": { "description": "Invoice export returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected export failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/generate": { "post": { "summary": "Generate recurring invoice", "description": "Generates one recurring invoice using canonical selector_input payload.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "create" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceSelectorBody" } } } }, "responses": { "201": { "description": "Invoice generated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid selector payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:create permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected generation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/manual": { "post": { "summary": "Create manual invoice", "description": "Creates a manual invoice using clientId + items payload.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "create" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceManualBody" } } } }, "responses": { "201": { "description": "Manual invoice created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid manual invoice payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:create permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected manual invoice failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/preview": { "post": { "summary": "Preview recurring invoice", "description": "Previews invoice output for selector_input payload without committing final invoice state.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceSelectorBody" } } } }, "responses": { "200": { "description": "Invoice preview returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid selector payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected preview failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/recurring": { "get": { "summary": "List recurring invoice templates", "description": "Current implementation returns an empty list placeholder (TODO in ApiInvoiceController.listRecurringTemplates).", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "recurring", "x-implementation-gap": "Returns [] placeholder." }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Recurring template list returned (currently empty).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:recurring permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected recurring template list failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } }, "post": { "summary": "Create recurring invoice template", "description": "Creates a recurring template used for scheduled invoice generation.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "recurring" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceRecurringCreateBody" } } } }, "responses": { "201": { "description": "Recurring template created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid recurring template payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:recurring permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected recurring template creation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/recurring/{id}": { "put": { "summary": "Update recurring invoice template", "description": "Current implementation echoes payload and id without persistence (TODO in ApiInvoiceController.updateRecurringTemplate).", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "recurring", "x-implementation-gap": "Update is TODO; response is synthetic." }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceRecurringCreateBody" } } } }, "responses": { "200": { "description": "Recurring template update response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid id or payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:recurring permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected recurring template update failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } }, "delete": { "summary": "Delete recurring invoice template", "description": "Current implementation is TODO and returns 204 without delete persistence.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "recurring", "x-implementation-gap": "Delete is TODO; returns 204 directly." }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Recurring template deleted (current TODO stub response)." }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:recurring permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected recurring template delete failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/search": { "get": { "summary": "Search invoices", "description": "Searches invoices by free-text q with paginated response.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_items", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_client", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_billing_cycle", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_transactions", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "q", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "to", "in": "query" }, { "schema": { "type": "string", "enum": [ "json", "csv" ] }, "required": false, "name": "format", "in": "query" } ], "responses": { "200": { "description": "Search results returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiPaginated" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected search failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/{id}": { "get": { "summary": "Get invoice by id", "description": "Returns one invoice with optional include flags.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_items", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_client", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_billing_cycle", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_transactions", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "q", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "to", "in": "query" }, { "schema": { "type": "string", "enum": [ "json", "csv" ] }, "required": false, "name": "format", "in": "query" } ], "responses": { "200": { "description": "Invoice operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid id or payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Invoice not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected invoice operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } }, "put": { "summary": "Update invoice", "description": "Updates an invoice using ApiBaseController.update().", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "update" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceUpdateBody" } } } }, "responses": { "200": { "description": "Invoice operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid id or payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:update permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Invoice not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected invoice operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } }, "delete": { "summary": "Delete invoice", "description": "Deletes one invoice using ApiBaseController.delete().", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "delete" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Invoice deleted." }, "400": { "description": "Invalid id or payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:delete permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Invoice not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected invoice operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/{id}/approve": { "post": { "summary": "Approve invoice", "description": "Approves one invoice; optional execution_id query is forwarded to workflow context.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "approve" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "execution_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "reason", "in": "query" } ], "responses": { "200": { "description": "Invoice action succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "201": { "description": "Invoice action created a new invoice resource.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid request payload or query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:approve permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Invoice not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected invoice action failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/{id}/credit": { "post": { "summary": "Apply credit to invoice", "description": "Applies credit amount to one invoice.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "credit" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceCreditBody" } } } }, "responses": { "200": { "description": "Invoice action succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "201": { "description": "Invoice action created a new invoice resource.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid request payload or query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:credit permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Invoice not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected invoice action failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/{id}/duplicate": { "post": { "summary": "Duplicate invoice", "description": "Clones an invoice into a new draft/manual invoice.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "create" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Invoice action succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "201": { "description": "Invoice action created a new invoice resource.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid request payload or query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:create permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Invoice not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected invoice action failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/{id}/finalize": { "post": { "summary": "Finalize invoice", "description": "Finalizes one invoice. invoice_id is path-derived and merged into finalize schema.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "finalize" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceFinalizeBody" } } } }, "responses": { "200": { "description": "Invoice action succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "201": { "description": "Invoice action created a new invoice resource.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid request payload or query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:finalize permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Invoice not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected invoice action failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/{id}/payment": { "post": { "summary": "Record invoice payment", "description": "Records payment for one invoice.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "payment" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoicePaymentBody" } } } }, "responses": { "200": { "description": "Invoice action succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "201": { "description": "Invoice action created a new invoice resource.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid request payload or query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:payment permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Invoice not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected invoice action failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/{id}/reject": { "post": { "summary": "Reject invoice", "description": "Rejects one invoice. Optional reason/execution_id come from query string.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "reject" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "execution_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "reason", "in": "query" } ], "responses": { "200": { "description": "Invoice action succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "201": { "description": "Invoice action created a new invoice resource.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid request payload or query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:reject permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Invoice not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected invoice action failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/{id}/send": { "post": { "summary": "Send invoice", "description": "Sends one invoice to provided recipient addresses.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "send" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceSendBody" } } } }, "responses": { "200": { "description": "Invoice action succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "201": { "description": "Invoice action created a new invoice resource.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid request payload or query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:send permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Invoice not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected invoice action failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/{id}/tax": { "post": { "summary": "Calculate invoice tax", "description": "Calculates tax for invoice context; uses billing permission gate.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceTaxBody" } } } }, "responses": { "200": { "description": "Invoice action succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "201": { "description": "Invoice action created a new invoice resource.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid request payload or query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:billing permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Invoice not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected invoice action failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/{id}/items": { "get": { "summary": "List invoice items", "description": "Returns invoice_charges array for one invoice id.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Invoice item list returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid invoice id.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Invoice not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected list-items failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/{id}/transactions": { "get": { "summary": "List invoice transactions", "description": "Returns transactions array for one invoice id.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Invoice transactions returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid invoice id.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:read permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Invoice not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected list-transactions failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/invoices/{id}/pdf": { "post": { "summary": "Generate invoice PDF asset", "description": "Generates/refreshes invoice PDF metadata and returns file_id plus optional download_url.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "pdf" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "responses": { "200": { "description": "PDF generation metadata returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiSuccess" } } } }, "400": { "description": "Invalid invoice id.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:pdf permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Invoice not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected PDF generation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } }, "get": { "summary": "Redirect to invoice PDF download", "description": "Attempts to generate/load PDF metadata and redirects to download_url when present.", "tags": [ "Invoices" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key header validated in ApiBaseController.authenticate()", "x-tenant-header": "x-tenant-id (optional; otherwise tenant inferred from key)", "x-rbac-resource": "invoice", "x-rbac-action": "pdf", "x-redirect-response": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath()." }, "required": true, "description": "Path UUID parameter resolved by ApiBaseController.extractIdFromPath().", "name": "id", "in": "path" } ], "responses": { "307": { "description": "Redirect to generated PDF URL." }, "400": { "description": "Invalid invoice id.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "403": { "description": "invoice:pdf permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "PDF URL unavailable or invoice not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "500": { "description": "Unexpected PDF download failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/financial": { "get": { "summary": "Financial API namespace root", "description": "Namespace root with no resource of its own — it returns 404. Use the financial sub-resources instead: /api/v1/financial/invoices, /api/v1/financial/transactions, /api/v1/financial/credits, /api/v1/financial/bulk/{invoices,transactions,credits}, and /api/v1/billing-analytics/overview.", "tags": [ "Financial" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-namespace-root": true }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } }, "404": { "description": "Always — this root has no resource; call a sub-resource path.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialInvoiceApiError" } } } } } } }, "/api/v1/kb-articles": { "get": { "summary": "List knowledge base articles", "description": "Returns a paginated list of KB articles for the tenant, with optional status, audience, article_type, category_id, and free-text search filters. Each row includes the joined document name.", "tags": [ "Knowledge Base" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "document", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "integer", "minimum": 1 }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "integer", "minimum": 1, "maximum": 100 }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "draft", "review", "published", "archived" ] }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string", "enum": [ "internal", "client", "public" ] }, "required": false, "name": "audience", "in": "query" }, { "schema": { "type": "string", "enum": [ "how_to", "faq", "troubleshooting", "reference" ] }, "required": false, "name": "article_type", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "category_id", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" } ], "responses": { "200": { "description": "Paginated KB articles.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedKbArticleResponse" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required document permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "Create a knowledge base article", "description": "Creates a KB article: generates a unique slug, creates the backing document record, and optionally stores initial block content. Returns the created article.", "tags": [ "Knowledge Base" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "document", "x-rbac-action": "create" }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KbArticleCreateRequest" } } } }, "responses": { "201": { "description": "Article created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KbArticleResponse" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required document permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/kb-articles/categories": { "get": { "summary": "List KB article categories", "description": "Returns the standard categories available for KB articles, ordered by display order then name.", "tags": [ "Knowledge Base" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "document", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "responses": { "200": { "description": "Available categories.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KbCategoryListResponse" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required document permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/kb-articles/templates": { "get": { "summary": "List KB article templates", "description": "Returns KB article templates for the tenant, optionally filtered by the article_type query parameter, ordered by name.", "tags": [ "Knowledge Base" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "document", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "enum": [ "how_to", "faq", "troubleshooting", "reference" ] }, "required": false, "name": "article_type", "in": "query" } ], "responses": { "200": { "description": "Available templates.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KbTemplateListResponse" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required document permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/kb-articles/from-ticket/{ticketId}": { "post": { "summary": "Create a KB article from a ticket", "description": "Creates a troubleshooting KB article (audience internal) from an existing ticket, seeding the body with the ticket title/description as a \"Problem\" section and the resolution as a \"Resolution\" section.", "tags": [ "Knowledge Base" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "document", "x-rbac-action": "create" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Source ticket UUID." }, "required": true, "description": "Source ticket UUID.", "name": "ticketId", "in": "path" } ], "responses": { "201": { "description": "Article created from the ticket.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KbArticleResponse" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required document permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Ticket not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/kb-articles/{id}": { "get": { "summary": "Get a knowledge base article", "description": "Returns a single KB article by id with its metadata, joined document name, and block content.", "tags": [ "Knowledge Base" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "document", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "KB article UUID." }, "required": true, "description": "KB article UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "The KB article.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KbArticleResponse" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required document permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Article not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "put": { "summary": "Update a knowledge base article", "description": "Updates article metadata (title, slug, type, audience, category, status, review cycle). Validates slug uniqueness, syncs the linked document name when the title changes, and recomputes the next review date when the review cycle changes.", "tags": [ "Knowledge Base" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "document", "x-rbac-action": "update" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "KB article UUID." }, "required": true, "description": "KB article UUID.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KbArticleUpdateRequest" } } } }, "responses": { "200": { "description": "Updated article.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KbArticleResponse" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required document permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Article not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "delete": { "summary": "Delete a knowledge base article", "description": "Deletes the KB article and cascades to remove the linked document and its block content.", "tags": [ "Knowledge Base" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "document", "x-rbac-action": "delete" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "KB article UUID." }, "required": true, "description": "KB article UUID.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Article deleted." }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required document permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Article not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/kb-articles/{id}/archive": { "post": { "summary": "Archive a knowledge base article", "description": "Sets the article status to archived and clears client visibility on the linked document. Returns the updated article.", "tags": [ "Knowledge Base" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "document", "x-rbac-action": "update" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "KB article UUID." }, "required": true, "description": "KB article UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Article archived.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KbArticleResponse" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required document permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Article not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/kb-articles/{id}/content": { "get": { "summary": "Get KB article content as text", "description": "Returns the article body converted from its stored block (BlockNote) content into readable markdown-like text (headings, lists, code blocks, paragraphs).", "tags": [ "Knowledge Base" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "document", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "KB article UUID." }, "required": true, "description": "KB article UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Rendered article content.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KbArticleContentResponse" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required document permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Article not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "put": { "summary": "Update KB article content", "description": "Replaces the article body. Accepts markdown or BlockNote JSON (format field), parses it into block content, and creates or updates the document block-content record.", "tags": [ "Knowledge Base" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "document", "x-rbac-action": "update" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "KB article UUID." }, "required": true, "description": "KB article UUID.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KbArticleContentUpdateRequest" } } } }, "responses": { "200": { "description": "Content updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KbArticleResponse" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required document permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Article not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/kb-articles/{id}/publish": { "post": { "summary": "Publish a knowledge base article", "description": "Sets the article status to published, records the publish timestamp and user, and sets is_client_visible on the linked document when the audience is client or public. Returns the updated article.", "tags": [ "Knowledge Base" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "document", "x-rbac-action": "update" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "KB article UUID." }, "required": true, "description": "KB article UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Article published.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KbArticleResponse" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required document permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Article not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/billing": { "get": { "summary": "Billing API namespace root", "description": "Namespace root with no resource of its own — it returns 404. Use /api/v1/financial/invoices, /api/v1/financial/transactions, /api/v1/financial/credits, and /api/v1/billing-analytics/overview.", "tags": [ "Billing" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-namespace-root": true }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Always — this root has no resource; call a sub-resource path.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/comments": { "get": { "summary": "Comments API namespace root", "description": "Namespace root with no resource of its own — it returns 404. Comments are accessed per ticket: /api/v1/tickets/{id}/comments and its sub-paths.", "tags": [ "Comments" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-namespace-root": true }, "x-alga-products": [ "psa", "algadesk" ], "responses": { "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Always — this root has no resource; call a sub-resource path.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/documents": { "get": { "summary": "Documents API namespace root", "description": "Namespace root with no resource of its own — it returns 404. Documents are accessed per parent entity, e.g. /api/v1/tickets/{id}/documents.", "tags": [ "Documents" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-namespace-root": true }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Always — this root has no resource; call a sub-resource path.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/email": { "get": { "summary": "Email API namespace root", "description": "Namespace root with no resource of its own — it returns 404. Use the email provider and webhook endpoints rather than this root.", "tags": [ "Email" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-namespace-root": true }, "x-alga-products": [ "psa", "algadesk" ], "responses": { "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Always — this root has no resource; call a sub-resource path.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/ai/document-assist": { "post": { "summary": "AI document assistance (streaming, EE)", "description": "Streams AI-generated document edits using the tenant's configured LLM provider. Enterprise Edition feature gated by the AI add-on tier and a feature flag; authenticated with the AI_DOCUMENT_API_KEY via the x-api-key header. Returns a streamed text response (501 on Community Edition).", "tags": [ "AI" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-edition-feature": "ee-ai", "x-streaming": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AiDocumentAssistBody" } } } }, "responses": { "200": { "description": "Streamed AI edit suggestions (text/event-stream).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicV1Success" } } } }, "401": { "description": "AI_DOCUMENT_API_KEY missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "AI add-on/feature flag not enabled.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "501": { "description": "Not available on Community Edition.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/software/search": { "get": { "summary": "Search software across the fleet", "description": "Searches installed software across all assets in the tenant, with filters for name/publisher search, category, software_type, is_managed, is_security_relevant, and client_id. Paginated. Authenticated by the global x-api-key middleware and tenant-scoped by the underlying withAuth action (it calls the searchSoftwareFleetWide server action directly rather than using a route-level controller wrapper).", "tags": [ "Software" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-via": "middleware-apikey+withAuth-action" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "category", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "software_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_managed", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_security_relevant", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "client_id", "in": "query" }, { "schema": { "type": "integer", "minimum": 1 }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "integer", "minimum": 1, "maximum": 200 }, "required": false, "name": "limit", "in": "query" } ], "responses": { "200": { "description": "Matching software, paginated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/priorities": { "get": { "summary": "List priorities", "description": "Lists ticket priorities for the tenant with pagination and sorting.", "tags": [ "Priorities" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "ticket", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "integer", "minimum": 1 }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "integer", "minimum": 1, "maximum": 100 }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" } ], "responses": { "200": { "description": "Priorities.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/priorities/{id}": { "get": { "summary": "Get a priority", "description": "Returns a single priority by id.", "tags": [ "Priorities" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "ticket", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "id", "in": "path" } ], "responses": { "200": { "description": "The priority.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Priority not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/extensions/install": { "post": { "summary": "Install an extension (EE)", "description": "Installs a tenant extension. Enterprise Edition feature requiring the extensions capability and the psa product; returns 501 on Community Edition.", "tags": [ "Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-edition-feature": "ee-extensions" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionActionBody" } } } }, "responses": { "200": { "description": "Extension install accepted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicV1Success" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Extensions capability/product not available.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "501": { "description": "Not available on Community Edition.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/extensions/uninstall": { "post": { "summary": "Uninstall an extension (EE)", "description": "Uninstalls a tenant extension. Enterprise Edition feature requiring the extensions capability and the psa product; returns 501 on Community Edition.", "tags": [ "Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-edition-feature": "ee-extensions" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtensionActionBody" } } } }, "responses": { "200": { "description": "Extension uninstall accepted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicV1Success" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Extensions capability/product not available.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "501": { "description": "Not available on Community Edition.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/company-contract-lines": { "get": { "summary": "List company contract lines (deprecated)", "description": "Lists client contract lines (contract_lines joined to client contracts), with filters and pagination. Deprecated: use /api/v1/client-contract-lines. This path is an alias kept for backwards compatibility.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-deprecated": true, "x-replaced-by": "/api/v1/client-contract-lines" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "integer", "minimum": 1 }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "integer", "minimum": 1, "maximum": 100 }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "client_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "contract_line_id", "in": "query" } ], "responses": { "200": { "description": "Client contract lines.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "Assign contract line to client (deprecated)", "description": "Assigns a contract line to a client by cloning a template line into the client contract. Deprecated: use /api/v1/client-contract-lines. This path is an alias kept for backwards compatibility.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-deprecated": true, "x-replaced-by": "/api/v1/client-contract-lines" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CompanyContractLineBody" } } } }, "responses": { "201": { "description": "Assignment created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/company-contract-lines/{id}": { "delete": { "summary": "Unassign contract line (deprecated)", "description": "Deactivates a client-owned contract line by id. Deprecated: use /api/v1/client-contract-lines. This path is an alias kept for backwards compatibility.", "tags": [ "Contract Lines" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-deprecated": true, "x-replaced-by": "/api/v1/client-contract-lines" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "id", "in": "path" } ], "responses": { "204": { "description": "Unassigned." }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/accounting-exports/xero-csv/client-export": { "get": { "summary": "Export clients as Xero Contacts CSV", "description": "Generates a Xero Contacts import CSV from the tenant clients (optionally limited to clientIds). Returns a CSV file. Requires billing:manage.", "tags": [ "Accounting Exports" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "billing", "x-rbac-action": "manage", "x-response-content-type": "text/csv" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Comma-separated client UUIDs to limit the export." }, "required": false, "description": "Comma-separated client UUIDs to limit the export.", "name": "clientIds", "in": "query" } ], "responses": { "200": { "description": "CSV file (text/csv attachment).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/accounting-exports/xero-csv/client-import": { "post": { "summary": "Import Xero Contacts CSV", "description": "Ingests a Xero Contacts CSV and matches/creates/updates clients. Accepts multipart file, JSON csvContent, or raw CSV. Supports preview mode and createNew/updateExisting/matchBy options. Requires billing:manage.", "tags": [ "Accounting Exports" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "billing", "x-rbac-action": "manage", "x-request-content-type": "multipart/form-data or application/json or text/csv" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "preview", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "createNew", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "updateExisting", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "matchBy", "in": "query" } ], "responses": { "200": { "description": "Import preview or result.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/accounting-exports/xero-csv/tax-import": { "post": { "summary": "Import Xero invoice tax CSV", "description": "Ingests a Xero Invoice Details Report CSV, extracts per-invoice tax amounts, and updates the matching Alga invoices. Accepts multipart file, JSON csvContent, or raw CSV; supports preview mode. Requires billing:manage.", "tags": [ "Accounting Exports" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "billing", "x-rbac-action": "manage", "x-request-content-type": "multipart/form-data or application/json or text/csv" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "preview", "in": "query" } ], "responses": { "200": { "description": "Import preview or result.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/accounting-exports/{batchId}/download": { "get": { "summary": "Download an accounting export batch", "description": "Regenerates and returns the export file (CSV/IIF) for a stored export batch using its registered adapter (xero_csv, quickbooks_desktop). Requires billing_settings:update.", "tags": [ "Accounting Exports" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "billing_settings", "x-rbac-action": "update" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Export batch identifier." }, "required": true, "description": "Export batch identifier.", "name": "batchId", "in": "path" } ], "responses": { "200": { "description": "Regenerated export file (attachment).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Batch not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/projects/templates": { "post": { "summary": "Create project template", "description": "Creates a project template from an existing project.", "tags": [ "Project Templates" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "project", "x-rbac-action": "create" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProjectTemplateCreateBody" } } } }, "responses": { "201": { "description": "Template created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "List project templates", "description": "Lists the tenant project templates.", "tags": [ "Project Templates" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "project", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "List project templates.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/projects/templates/{templateId}": { "patch": { "summary": "Update project template", "description": "Updates the template name, description, or category.", "tags": [ "Project Templates" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "project", "x-rbac-action": "update" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "templateId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProjectTemplateUpdateBody" } } } }, "responses": { "200": { "description": "Template updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Template not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "Get a project template", "description": "Returns a single project template by id.", "tags": [ "Project Templates" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "project", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "templateId", "in": "path" } ], "responses": { "200": { "description": "Get a project template.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Template not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "delete": { "summary": "Delete a project template", "description": "Deletes a project template by id.", "tags": [ "Project Templates" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "project", "x-rbac-action": "delete" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "templateId", "in": "path" } ], "responses": { "204": { "description": "Template deleted." }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Template not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/projects/templates/{templateId}/apply": { "post": { "summary": "Create project from template", "description": "Creates a new project from the template, copying the selected parts (phases, statuses, tasks, etc.).", "tags": [ "Project Templates" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "project", "x-rbac-action": "create" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "templateId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProjectTemplateApplyBody" } } } }, "responses": { "201": { "description": "Project created from template.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Template not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/projects/templates/{templateId}/duplicate": { "post": { "summary": "Duplicate project template", "description": "Creates a complete copy of the template and returns the new template id. No request body.", "tags": [ "Project Templates" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "project", "x-rbac-action": "create" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "templateId", "in": "path" } ], "responses": { "201": { "description": "Template duplicated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Template not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-definitions": { "post": { "summary": "Create workflow definition", "description": "Creates a new workflow with a draft definition.", "tags": [ "Workflow Definitions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "manage" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowDefinitionCreateBody" } } } }, "responses": { "201": { "description": "Workflow definition created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "List workflow definitions", "description": "Lists workflow definitions for the tenant.", "tags": [ "Workflow Definitions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "List workflow definitions.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-definitions/import": { "post": { "summary": "Import a v1 workflow bundle", "description": "Imports a legacy v1 workflow bundle. Pass force=true (query) to overwrite an existing definition.", "tags": [ "Workflow Definitions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "admin" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "force", "in": "query" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowImportBody" } } } }, "responses": { "201": { "description": "Bundle imported.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-definitions/{workflowId}/metadata": { "put": { "summary": "Update workflow metadata", "description": "Updates workflow metadata: key, visibility, pause state, concurrency limit, failure thresholds, and retention policy.", "tags": [ "Workflow Definitions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "publish" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "workflowId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowMetadataBody" } } } }, "responses": { "200": { "description": "Metadata updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Workflow not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-definitions/{workflowId}/{version}": { "put": { "summary": "Update workflow draft definition", "description": "Replaces the draft definition for a specific version.", "tags": [ "Workflow Definitions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "manage" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "workflowId", "in": "path" }, { "schema": { "type": "string", "description": "Draft version number (coerced to a positive integer)." }, "required": true, "description": "Draft version number (coerced to a positive integer).", "name": "version", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowDefinitionUpdateBody" } } } }, "responses": { "200": { "description": "Draft updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Workflow/version not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "Get a workflow version", "description": "Returns the definition document for a specific workflow version.", "tags": [ "Workflow Definitions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "workflowId", "in": "path" }, { "schema": { "type": "string", "description": "Draft version number (coerced to a positive integer)." }, "required": true, "description": "Draft version number (coerced to a positive integer).", "name": "version", "in": "path" } ], "responses": { "200": { "description": "Get a workflow version.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Workflow/version not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-definitions/{workflowId}/{version}/publish": { "post": { "summary": "Publish a workflow version", "description": "Publishes the draft as an active version (auto-increments when the requested version is below the next expected). An optional definition in the body overrides the stored draft.", "tags": [ "Workflow Definitions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "publish" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "workflowId", "in": "path" }, { "schema": { "type": "string", "description": "Draft version number (coerced to a positive integer)." }, "required": true, "description": "Draft version number (coerced to a positive integer).", "name": "version", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowPublishBody" } } } }, "responses": { "200": { "description": "Version published.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Workflow/version not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs": { "post": { "summary": "Start a workflow run", "description": "Starts a workflow execution with an optional version override and input payload (payload capped ~2MB; rate-limited per tenant).", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "manage" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowRunCreateBody" } } } }, "responses": { "201": { "description": "Run started.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "List workflow runs", "description": "Lists workflow runs for the tenant.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "List workflow runs.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/{runId}/cancel": { "post": { "summary": "Cancel a workflow run", "description": "Stops the run and marks its waits as canceled.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "admin" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": true, "name": "runId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowRunActionBody" } } } }, "responses": { "200": { "description": "Cancel a workflow run succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Run not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/{runId}/replay": { "post": { "summary": "Replay a workflow run", "description": "Re-executes the workflow from the start with a new input payload.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "admin" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": true, "name": "runId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowRunReplayBody" } } } }, "responses": { "200": { "description": "Replay a workflow run succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Run not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/{runId}/requeue": { "post": { "summary": "Requeue a workflow run", "description": "Re-initializes the run's event wait (for runs stuck awaiting an external event).", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "admin" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": true, "name": "runId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowRunActionBody" } } } }, "responses": { "200": { "description": "Requeue a workflow run succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Run not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/{runId}/resume": { "post": { "summary": "Resume a workflow run", "description": "Resolves waiting steps and resumes a paused/waiting run.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "admin" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": true, "name": "runId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowRunActionBody" } } } }, "responses": { "200": { "description": "Resume a workflow run succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Run not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/{runId}/retry": { "post": { "summary": "Retry a failed workflow run", "description": "Restarts a FAILED run from its last failed node.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "admin" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": true, "name": "runId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowRunActionBody" } } } }, "responses": { "200": { "description": "Retry a failed workflow run succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Run not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow/events": { "post": { "summary": "Submit a workflow event", "description": "Injects an event into the workflow runtime, correlating it to waiting runs by event name and correlation key.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "manage" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowEventBody" } } } }, "responses": { "200": { "description": "Event accepted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "List workflow events", "description": "Lists submitted workflow events for the tenant.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "List workflow events.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/projects/templates/categories": { "get": { "summary": "List project template categories", "description": "Lists the available project-template categories.", "tags": [ "Project Templates" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "project", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "List project template categories.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-definitions/{workflowId}/versions": { "get": { "summary": "List workflow versions", "description": "Lists the versions (draft and published) of a workflow definition.", "tags": [ "Workflow Definitions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "workflowId", "in": "path" } ], "responses": { "200": { "description": "List workflow versions.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Workflow not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-definitions/{workflowId}/export": { "get": { "summary": "Export a workflow definition", "description": "Exports the workflow definition document (JSON).", "tags": [ "Workflow Definitions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "workflowId", "in": "path" } ], "responses": { "200": { "description": "Export a workflow definition.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Workflow not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-definitions/{workflowId}/audit": { "get": { "summary": "Get workflow audit log", "description": "Returns the audit-log entries for a workflow definition.", "tags": [ "Workflow Definitions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "workflowId", "in": "path" } ], "responses": { "200": { "description": "Get workflow audit log.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Workflow not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-definitions/{workflowId}/audit/export": { "get": { "summary": "Export workflow audit log", "description": "Exports the workflow definition audit log.", "tags": [ "Workflow Definitions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "workflowId", "in": "path" } ], "responses": { "200": { "description": "Export workflow audit log.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Workflow not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/dead-letter": { "get": { "summary": "List dead-lettered runs", "description": "Lists runs that failed terminally and were moved to the dead-letter set.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "List dead-lettered runs.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/export": { "get": { "summary": "Export workflow runs", "description": "Exports workflow runs (JSON/CSV).", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Export workflow runs.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/latest": { "get": { "summary": "Get latest workflow runs", "description": "Returns the most recent workflow runs.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Get latest workflow runs.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/summary": { "get": { "summary": "Get workflow runs summary", "description": "Returns aggregate run metrics across the tenant.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Get workflow runs summary.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/{runId}": { "get": { "summary": "Get a workflow run", "description": "Returns a single workflow run by id.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": true, "name": "runId", "in": "path" } ], "responses": { "200": { "description": "Get a workflow run.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Run not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/{runId}/steps": { "get": { "summary": "Get workflow run steps", "description": "Returns the step/node states for a run.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": true, "name": "runId", "in": "path" } ], "responses": { "200": { "description": "Get workflow run steps.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Run not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/{runId}/timeline": { "get": { "summary": "Get workflow run timeline", "description": "Returns the chronological event timeline for a run.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": true, "name": "runId", "in": "path" } ], "responses": { "200": { "description": "Get workflow run timeline.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Run not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/{runId}/summary": { "get": { "summary": "Get workflow run summary", "description": "Returns a summary of a single run.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": true, "name": "runId", "in": "path" } ], "responses": { "200": { "description": "Get workflow run summary.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Run not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/{runId}/audit": { "get": { "summary": "Get workflow run audit log", "description": "Returns the audit-log entries for a run.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": true, "name": "runId", "in": "path" } ], "responses": { "200": { "description": "Get workflow run audit log.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Run not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/{runId}/audit/export": { "get": { "summary": "Export workflow run audit log", "description": "Exports the audit log for a run.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": true, "name": "runId", "in": "path" } ], "responses": { "200": { "description": "Export workflow run audit log.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Run not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow-runs/{runId}/export": { "get": { "summary": "Export a workflow run", "description": "Exports a single workflow run (JSON).", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": true, "name": "runId", "in": "path" } ], "responses": { "200": { "description": "Export a workflow run.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Run not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow/events/export": { "get": { "summary": "Export workflow events", "description": "Exports workflow events (JSON/CSV).", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Export workflow events.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow/events/summary": { "get": { "summary": "Get workflow events summary", "description": "Returns aggregate workflow-event metrics.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Get workflow events summary.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow/events/{eventId}": { "get": { "summary": "Get a workflow event", "description": "Returns a single workflow event by id.", "tags": [ "Workflow Runs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": true, "name": "eventId", "in": "path" } ], "responses": { "200": { "description": "Get a workflow event.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnversionedV1Success" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Event not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow/registry/actions": { "get": { "summary": "List workflow registry actions", "description": "Lists every action registered in the workflow runtime — the building blocks workflow definitions can invoke — including JSON Schemas for each action's input and output. Returns a bare array (no envelope).", "tags": [ "Workflow Registry" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Registered actions.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/WorkflowRegistryAction" } } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow/registry/nodes": { "get": { "summary": "List workflow registry node types", "description": "Lists the node types available to workflow definitions (triggers, control flow, action nodes, etc.) with each node's configuration JSON Schema and default retry policy. Returns a bare array (no envelope).", "tags": [ "Workflow Registry" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Registered node types.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/WorkflowRegistryNode" } } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow/registry/designer-catalog": { "get": { "summary": "Get the workflow designer action catalog", "description": "Returns the action catalog the workflow designer renders: registry actions and integration modules grouped into tiles, filtered to the integrations actually available to the tenant. Returns a bare array of catalog records (no envelope).", "tags": [ "Workflow Registry" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Designer catalog tiles.", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "object", "additionalProperties": {}, "description": "Catalog tile (groupKey, tileKind, actions, UI metadata)." } } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/workflow/registry/schemas/{schemaRef}": { "get": { "summary": "Get a workflow schema by ref", "description": "Resolves a registered workflow schema reference (URL-encoded) to its JSON Schema document.", "tags": [ "Workflow Registry" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "workflow", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Registered schema reference; URL-encode it in the path." }, "required": true, "description": "Registered schema reference; URL-encode it in the path.", "name": "schemaRef", "in": "path" } ], "responses": { "200": { "description": "Resolved schema.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowSchemaResponse" } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Unknown schema ref.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/projects": { "get": { "summary": "List projects", "description": "Lists all projects for the tenant. Returns a bare array of project records (no envelope); responds 403 with an error message when the caller lacks project read permission.", "tags": [ "Projects" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "project", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Projects for the tenant.", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "object", "additionalProperties": {}, "description": "Project record." } } } } }, "400": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Caller lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/files/{fileId}/download": { "get": { "summary": "Download file by ID", "description": "Downloads a stored external file as an attachment by external_files.file_id. The route is skipped by API-key middleware and relies on session/tenant resolution through createTenantKnex and StorageService. Storage lookup is tenant-scoped to external_files.tenant and is_deleted=false, but the handler performs no explicit per-document authorization; any user resolved into the tenant can download a file by fileId. The response body is binary and Content-Type is taken from stored file metadata.", "tags": [ "Files" ], "security": [ { "SessionCookieAuth": [] } ], "extensions": { "x-api-key-auth-skipped": true, "x-tenant-scoped-storage": true, "x-explicit-document-authorization": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "File UUID from external_files.file_id to download. The handler does not validate UUID syntax before querying storage metadata." }, "required": true, "description": "File UUID from external_files.file_id to download. The handler does not validate UUID syntax before querying storage metadata.", "name": "fileId", "in": "path" } ], "responses": { "200": { "description": "Binary file download with attachment Content-Disposition. Content-Type varies by stored MIME type.", "content": { "application/octet-stream": { "schema": { "$ref": "#/components/schemas/FileBinaryDownloadResponse" } } } }, "404": { "description": "Tenant could not be resolved by createTenantKnex.", "content": { "text/plain": { "schema": { "$ref": "#/components/schemas/FileDownloadPlainTextError" } } } }, "500": { "description": "File metadata/storage lookup failed or another download error occurred.", "content": { "text/plain": { "schema": { "$ref": "#/components/schemas/FileDownloadPlainTextError" } } } } } } }, "/api/v1/inbound-webhooks": { "get": { "summary": "List inbound webhooks", "description": "Lists tenant inbound webhook configurations.", "tags": [ "Inbound Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "withAuth server action session context", "x-rbac-resource": "inbound_webhook", "x-rbac-action": "read", "x-handler": "server/src/app/api/v1/inbound-webhooks/route.ts" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Inbound webhook list returned.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/InboundWebhookConfig" } } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid inbound webhook request.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "401": { "description": "Missing or invalid authenticated user context.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "403": { "description": "Inbound webhook permission denied.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "500": { "description": "Unexpected inbound webhook operation failure.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } } } }, "post": { "summary": "Create inbound webhook", "description": "Creates an inbound webhook configuration and returns any one-time generated secret.", "tags": [ "Inbound Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "withAuth server action session context", "x-rbac-resource": "inbound_webhook", "x-rbac-action": "create", "x-handler": "server/src/app/api/v1/inbound-webhooks/route.ts" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InboundWebhookCreateInput" } } } }, "responses": { "201": { "description": "Inbound webhook created.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/InboundWebhookConfig" }, "secret": { "type": [ "string", "null" ] } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid inbound webhook request.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "401": { "description": "Missing or invalid authenticated user context.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "403": { "description": "Inbound webhook permission denied.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "409": { "description": "Inbound webhook slug already exists.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "500": { "description": "Unexpected inbound webhook operation failure.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } } } } }, "/api/v1/inbound-webhooks/actions": { "get": { "summary": "List inbound webhook actions", "description": "Returns registered direct-action definitions with target field schemas.", "tags": [ "Inbound Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "withAuth server action session context", "x-rbac-resource": "inbound_webhook", "x-rbac-action": "read", "x-handler": "server/src/app/api/v1/inbound-webhooks/actions/route.ts" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Inbound action definitions returned.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/InboundActionDefinition" } } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid inbound webhook request.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "401": { "description": "Missing or invalid authenticated user context.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "403": { "description": "Inbound webhook permission denied.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "500": { "description": "Unexpected inbound webhook operation failure.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } } } } }, "/api/v1/inbound-webhooks/{id}": { "get": { "summary": "Get inbound webhook", "description": "Returns a single inbound webhook configuration.", "tags": [ "Inbound Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "withAuth server action session context", "x-rbac-resource": "inbound_webhook", "x-rbac-action": "read", "x-handler": "server/src/app/api/v1/inbound-webhooks/{id}/route.ts" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Inbound webhook UUID." }, "required": true, "description": "Inbound webhook UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Inbound webhook returned.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/InboundWebhookConfig" }, "secret": { "type": [ "string", "null" ] } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid inbound webhook request.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "401": { "description": "Missing or invalid authenticated user context.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "403": { "description": "Inbound webhook permission denied.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "404": { "description": "Inbound webhook not found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "500": { "description": "Unexpected inbound webhook operation failure.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } } } }, "put": { "summary": "Update inbound webhook", "description": "Updates an inbound webhook configuration by id.", "tags": [ "Inbound Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "withAuth server action session context", "x-rbac-resource": "inbound_webhook", "x-rbac-action": "update", "x-handler": "server/src/app/api/v1/inbound-webhooks/{id}/route.ts" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Inbound webhook UUID." }, "required": true, "description": "Inbound webhook UUID.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InboundWebhookUpdateInput" } } } }, "responses": { "200": { "description": "Inbound webhook updated.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/InboundWebhookConfig" }, "secret": { "type": [ "string", "null" ] } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid inbound webhook request.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "401": { "description": "Missing or invalid authenticated user context.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "403": { "description": "Inbound webhook permission denied.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "404": { "description": "Inbound webhook not found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "500": { "description": "Unexpected inbound webhook operation failure.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } } } }, "delete": { "summary": "Delete inbound webhook", "description": "Deletes an inbound webhook configuration by id.", "tags": [ "Inbound Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "withAuth server action session context", "x-rbac-resource": "inbound_webhook", "x-rbac-action": "delete", "x-handler": "server/src/app/api/v1/inbound-webhooks/{id}/route.ts" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Inbound webhook UUID." }, "required": true, "description": "Inbound webhook UUID.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Inbound webhook deleted." }, "400": { "description": "Invalid inbound webhook request.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "401": { "description": "Missing or invalid authenticated user context.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "403": { "description": "Inbound webhook permission denied.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "404": { "description": "Inbound webhook not found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "500": { "description": "Unexpected inbound webhook operation failure.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } } } } }, "/api/v1/inbound-webhooks/{id}/rotate-secret": { "post": { "summary": "Rotate inbound webhook secret", "description": "Rotates the inbound webhook authentication secret and returns the replacement once.", "tags": [ "Inbound Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "withAuth server action session context", "x-rbac-resource": "inbound_webhook", "x-rbac-action": "update", "x-handler": "server/src/app/api/v1/inbound-webhooks/{id}/rotate-secret/route.ts" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Inbound webhook UUID." }, "required": true, "description": "Inbound webhook UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Inbound webhook secret rotated.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/InboundWebhookConfig" }, "secret": { "type": [ "string", "null" ] } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid inbound webhook request.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "401": { "description": "Missing or invalid authenticated user context.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "403": { "description": "Inbound webhook permission denied.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "404": { "description": "Inbound webhook not found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "500": { "description": "Unexpected inbound webhook operation failure.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } } } } }, "/api/v1/inbound-webhooks/{id}/test": { "post": { "summary": "Send inbound webhook test request", "description": "Dispatches a synthetic inbound request through the current webhook configuration.", "tags": [ "Inbound Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "withAuth server action session context", "x-rbac-resource": "inbound_webhook", "x-rbac-action": "update", "x-handler": "server/src/app/api/v1/inbound-webhooks/{id}/test/route.ts" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Inbound webhook UUID." }, "required": true, "description": "Inbound webhook UUID.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "body": {}, "headers": { "type": "object", "additionalProperties": { "anyOf": [ { "type": "string" }, { "type": "array", "items": { "type": "string" } } ] } } } } } } }, "responses": { "200": { "description": "Synthetic inbound delivery accepted.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/InboundWebhookDelivery" } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid inbound webhook request.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "401": { "description": "Missing or invalid authenticated user context.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "403": { "description": "Inbound webhook permission denied.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "404": { "description": "Inbound webhook not found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "500": { "description": "Unexpected inbound webhook operation failure.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } } } } }, "/api/v1/inbound-webhooks/{id}/capture-sample": { "post": { "summary": "Enable inbound webhook sample capture", "description": "Enables capture mode so the next verified request stores its body as the sample payload.", "tags": [ "Inbound Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "withAuth server action session context", "x-rbac-resource": "inbound_webhook", "x-rbac-action": "update", "x-handler": "server/src/app/api/v1/inbound-webhooks/{id}/capture-sample/route.ts" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Inbound webhook UUID." }, "required": true, "description": "Inbound webhook UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Sample capture enabled.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/InboundWebhookConfig" }, "secret": { "type": [ "string", "null" ] } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid inbound webhook request.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "401": { "description": "Missing or invalid authenticated user context.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "403": { "description": "Inbound webhook permission denied.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "404": { "description": "Inbound webhook not found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "500": { "description": "Unexpected inbound webhook operation failure.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } } } }, "delete": { "summary": "Clear inbound webhook sample payload", "description": "Clears the saved sample payload and disables sample capture.", "tags": [ "Inbound Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "withAuth server action session context", "x-rbac-resource": "inbound_webhook", "x-rbac-action": "update", "x-handler": "server/src/app/api/v1/inbound-webhooks/{id}/capture-sample/route.ts" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Inbound webhook UUID." }, "required": true, "description": "Inbound webhook UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Sample payload cleared.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/InboundWebhookConfig" }, "secret": { "type": [ "string", "null" ] } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid inbound webhook request.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "401": { "description": "Missing or invalid authenticated user context.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "403": { "description": "Inbound webhook permission denied.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "404": { "description": "Inbound webhook not found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "500": { "description": "Unexpected inbound webhook operation failure.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } } } } }, "/api/v1/inbound-webhooks/{id}/deliveries": { "get": { "summary": "List inbound webhook deliveries", "description": "Returns paginated delivery history for one inbound webhook.", "tags": [ "Inbound Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "withAuth server action session context", "x-rbac-resource": "inbound_webhook", "x-rbac-action": "read", "x-handler": "server/src/app/api/v1/inbound-webhooks/{id}/deliveries/route.ts" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Inbound webhook UUID." }, "required": true, "description": "Inbound webhook UUID.", "name": "id", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "pending", "dispatched", "duplicate", "failed" ] }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "date_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "date_to", "in": "query" } ], "responses": { "200": { "description": "Inbound delivery list returned.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/InboundWebhookDelivery" } }, "meta": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" } }, "required": [ "page", "limit", "total" ] } }, "required": [ "data", "meta" ] } } } }, "400": { "description": "Invalid inbound webhook request.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "401": { "description": "Missing or invalid authenticated user context.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "403": { "description": "Inbound webhook permission denied.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "404": { "description": "Inbound webhook not found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "500": { "description": "Unexpected inbound webhook operation failure.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } } } } }, "/api/v1/inbound-webhooks/{id}/deliveries/{deliveryId}": { "get": { "summary": "Get inbound webhook delivery", "description": "Returns a single inbound webhook delivery row including request and response details.", "tags": [ "Inbound Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "withAuth server action session context", "x-rbac-resource": "inbound_webhook", "x-rbac-action": "read", "x-handler": "server/src/app/api/v1/inbound-webhooks/{id}/deliveries/{deliveryId}/route.ts" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Inbound webhook UUID." }, "required": true, "description": "Inbound webhook UUID.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Inbound delivery UUID." }, "required": true, "description": "Inbound delivery UUID.", "name": "deliveryId", "in": "path" } ], "responses": { "200": { "description": "Inbound delivery returned.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/InboundWebhookDelivery" } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid inbound webhook request.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "401": { "description": "Missing or invalid authenticated user context.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "403": { "description": "Inbound webhook permission denied.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "404": { "description": "Inbound delivery not found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "500": { "description": "Unexpected inbound webhook operation failure.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } } } } }, "/api/v1/inbound-webhooks/{id}/deliveries/{deliveryId}/replay": { "post": { "summary": "Replay inbound webhook delivery", "description": "Re-dispatches a stored inbound delivery against the current webhook configuration.", "tags": [ "Inbound Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "withAuth server action session context", "x-rbac-resource": "inbound_webhook", "x-rbac-action": "replay", "x-handler": "server/src/app/api/v1/inbound-webhooks/{id}/deliveries/{deliveryId}/replay/route.ts" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Inbound webhook UUID." }, "required": true, "description": "Inbound webhook UUID.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Inbound delivery UUID." }, "required": true, "description": "Inbound delivery UUID.", "name": "deliveryId", "in": "path" } ], "responses": { "200": { "description": "Inbound delivery replay accepted.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/InboundWebhookDelivery" } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid inbound webhook request.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "401": { "description": "Missing or invalid authenticated user context.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "403": { "description": "Inbound webhook permission denied.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "404": { "description": "Inbound delivery not found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "500": { "description": "Unexpected inbound webhook operation failure.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } } } } }, "/api/inbound/{tenantSlug}/{webhookSlug}": { "post": { "summary": "Receive inbound webhook payload", "description": "Receives a JSON payload for a tenant inbound webhook. Authentication, idempotency, rate limit, and handler behavior are controlled by the webhook configuration.", "tags": [ "Inbound Webhooks" ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "per-webhook auth configuration", "x-rbac-resource": "inbound_webhook", "x-handler": "server/src/app/api/inbound/[tenantSlug]/[webhookSlug]/route.ts" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "URL-safe tenant slug used for inbound webhook routing." }, "required": true, "description": "URL-safe tenant slug used for inbound webhook routing.", "name": "tenantSlug", "in": "path" }, { "schema": { "type": "string", "description": "URL-safe inbound webhook slug unique within the tenant." }, "required": true, "description": "URL-safe inbound webhook slug unique within the tenant.", "name": "webhookSlug", "in": "path" }, { "schema": { "type": "string", "description": "Expected to be application/json for JSON payloads." }, "required": false, "description": "Expected to be application/json for JSON payloads.", "name": "content-type", "in": "header" }, { "schema": { "type": "string", "description": "Example HMAC-SHA256 signature header. Actual header name is webhook-configurable." }, "required": false, "description": "Example HMAC-SHA256 signature header. Actual header name is webhook-configurable.", "name": "x-signature", "in": "header" }, { "schema": { "type": "string", "description": "Optional idempotency key header when the webhook uses a header idempotency source." }, "required": false, "description": "Optional idempotency key header when the webhook uses a header idempotency source.", "name": "x-idempotency-key", "in": "header" } ], "requestBody": { "description": "Source-specific JSON payload. Shape varies per inbound webhook configuration.", "required": true, "content": { "application/json": { "schema": {}, "description": "Source-specific JSON payload. Shape varies per inbound webhook configuration." } } }, "responses": { "200": { "description": "Request accepted or treated as duplicate no-op.", "content": { "application/json": { "schema": { "type": "object", "properties": { "delivery_id": { "type": "string", "format": "uuid" } }, "required": [ "delivery_id" ] } } } }, "400": { "description": "Invalid request payload or handler validation failure.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "401": { "description": "Authentication failed or receiver is unknown/inactive. Response body is intentionally empty to avoid leaking which webhooks exist." }, "429": { "description": "Per-webhook rate limit exceeded.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } }, "500": { "description": "Unexpected dispatch failure.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} }, "required": [ "code", "message" ] } }, "required": [ "error" ] } } } } } }, "patch": { "summary": "PATCH inbound", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "inbound" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "put": { "summary": "PUT inbound", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "inbound" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/health": { "get": { "summary": "Health check", "description": "Simple unauthenticated liveness check for the Next.js API process. It performs no database, Redis, or downstream dependency checks and returns a fixed status/version payload when the process can serve HTTP requests.", "tags": [ "System" ], "security": [], "extensions": { "x-api-key-auth-skipped": true, "x-dependency-checks": false }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "API process is alive and responding.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthResponse" } } } } } } }, "/api/healthz": { "get": { "summary": "Liveness probe", "description": "Kubernetes liveness endpoint. It returns a fixed healthy status with timestamp, uptime, version, and environment when the process can serve HTTP. It does not check database, Redis, or downstream dependencies; use /api/readyz for dependency-aware readiness.", "tags": [ "System" ], "security": [], "extensions": { "x-api-key-auth-skipped": true, "x-dependency-checks": false, "x-kubernetes-probe": "liveness" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Process is alive and serving HTTP.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthzResponse" } } } } } } }, "/api/readyz": { "get": { "summary": "Readiness probe", "description": "Kubernetes readiness endpoint for the Next.js API route. It checks database connectivity with SELECT 1 and returns 200 only when critical dependencies are available. The Redis check is currently a placeholder that mirrors the database result. This endpoint is unauthenticated and is explicitly skipped by API-key middleware.", "tags": [ "System" ], "security": [], "extensions": { "x-api-key-auth-skipped": true, "x-dependency-checks": true, "x-kubernetes-probe": "readiness", "x-redis-check-stubbed": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "All critical dependencies checked by this handler are available.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReadyzReadyResponse" } } } }, "503": { "description": "One or more critical dependencies are unavailable, or a readiness check threw an exception.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReadyzNotReadyResponse" } } } } } } }, "/api/installs/lookup-by-host": { "get": { "summary": "Lookup extension install by runner host", "description": "Internal endpoint for extension runners. Given a runner domain, resolves the tenant, extension registry ID, and current bundle content hash needed to serve extension content. The EE implementation queries tenant_extension_install.runner_domain with an admin connection and then selects the latest extension_bundle row for the installed version. In non-EE builds the product-extension action is a stub. Requires x-api-key; the Express middleware allows the ALGA_AUTH_KEY runner secret or a valid database API key.", "tags": [ "Extension Installs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-runner-internal": true, "x-admin-db-connection": true, "x-cache-control": "no-store", "x-vary": "x-api-key, x-canary" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "minLength": 1, "description": "Runner domain hostname to resolve. The implementation lowercases the value and strips any port before matching tenant_extension_install.runner_domain." }, "required": true, "description": "Runner domain hostname to resolve. The implementation lowercases the value and strips any port before matching tenant_extension_install.runner_domain.", "name": "host", "in": "query" }, { "schema": { "type": "string", "description": "Optional runner canary identifier used only for logging and cache variance." }, "required": false, "description": "Optional runner canary identifier used only for logging and cache variance.", "name": "x-canary", "in": "header" } ], "responses": { "200": { "description": "Install mapping found for the runner host.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InstallLookupByHostResponse" } } } }, "400": { "description": "The required host query parameter is missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InstallLookupErrorResponse" } } } }, "401": { "description": "x-api-key is missing or invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InstallLookupErrorResponse" } } } }, "404": { "description": "No install or bundle was found for the runner host.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InstallLookupErrorResponse" } } } }, "500": { "description": "Unexpected lookup failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InstallLookupErrorResponse" } } } } } } }, "/api/installs/validate": { "get": { "summary": "Validate extension bundle content hash", "description": "Internal endpoint for extension runners and deployment validation. It checks whether a supplied content hash belongs to the currently installed extension version for the supplied tenant and extension registry ID. The EE action uses an admin database connection, reads tenant_extension_install by tenant_id and registry_id, and checks extension_bundle for a matching version_id and content_hash. A negative validation result is returned as 200 with valid=false, not as 404. Requires x-api-key; the Express middleware allows the ALGA_AUTH_KEY runner secret or a valid database API key.", "tags": [ "Extension Installs" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-runner-internal": true, "x-admin-db-connection": true, "x-cache-control": "no-store", "x-vary": "x-api-key, x-canary" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Tenant UUID from tenant_extension_install.tenant_id." }, "required": true, "description": "Tenant UUID from tenant_extension_install.tenant_id.", "name": "tenant", "in": "query" }, { "schema": { "type": "string", "format": "uuid", "description": "Extension registry UUID from tenant_extension_install.registry_id." }, "required": true, "description": "Extension registry UUID from tenant_extension_install.registry_id.", "name": "extension", "in": "query" }, { "schema": { "type": "string", "minLength": 1, "description": "Bundle content hash to validate. The EE action accepts sha256:<64 hex chars> or a raw 64-character hex string and normalizes raw hex to sha256: form." }, "required": true, "description": "Bundle content hash to validate. The EE action accepts sha256:<64 hex chars> or a raw 64-character hex string and normalizes raw hex to sha256: form.", "name": "hash", "in": "query" }, { "schema": { "type": "string", "description": "Optional runner canary identifier used only for logging and cache variance." }, "required": false, "description": "Optional runner canary identifier used only for logging and cache variance.", "name": "x-canary", "in": "header" } ], "responses": { "200": { "description": "Validation completed. valid=false means the hash did not match, the install was not found, or the hash format was invalid at the action layer.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InstallValidateResponse" } } } }, "400": { "description": "Required tenant, extension, or hash query parameter is missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InstallValidateParameterErrorResponse" } } } }, "401": { "description": "x-api-key is missing or invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InstallLookupErrorResponse" } } } }, "500": { "description": "Unexpected validation failure. The EE handler returns valid=false for internal exceptions.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InstallValidateResponse" } } } } } } }, "/api/integrations/qbo/status": { "get": { "summary": "QuickBooks Online status route unavailable", "description": "This path appears in the generated route inventory, but server/src/app/api/integrations/qbo/status/route.ts is not present in the current worktree. Without an x-api-key, API middleware returns 401 before routing. With the middleware requirement satisfied, Next.js has no handler for this path and returns the framework not-found response. QuickBooks connection status is currently exposed through server actions such as getQboConnectionStatus and through versioned QuickBooks API routes, not this inventory-only path.", "deprecated": true, "tags": [ "Integrations - QuickBooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/integrations/qbo/status/route.ts", "x-oauth-provider": "quickbooks-online" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key is missing at middleware before routing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QboErrorResponse" } } } }, "404": { "description": "No route handler exists for this inventory-only path in the current worktree.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/QboRouteMissingResponse" } } } } } } }, "/api/integrations/qbo/connect": { "get": { "summary": "Start QuickBooks Online OAuth flow", "description": "Browser-navigated endpoint that initiates the QuickBooks Online OAuth 2.0 authorization flow. The handler resolves tenant context with createTenantKnex, reads qbo_client_id from app secrets, generates a random CSRF value, embeds {tenantId, csrf} as base64url JSON in the state parameter, and redirects to Intuit at appcenter.intuit.com/connect/oauth2. The CSRF value is currently logged but not persisted, and the callback currently treats CSRF as valid. The route is not currently listed in apiKeySkipPaths, so middleware may require x-api-key before the browser can reach the handler.", "tags": [ "Integrations - QuickBooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-oauth-provider": "quickbooks-online", "x-redirect-endpoint": true, "x-middleware-api-key-required-currently": true, "x-csrf-validation-implemented": false }, "x-alga-products": [ "psa" ], "responses": { "302": { "description": "Redirect to Intuit OAuth authorization URL. Location includes client_id, response_type=code, QuickBooks accounting scope, redirect_uri, and encoded state. The handler returns no JSON body." }, "401": { "description": "Tenant context is missing in the handler, or x-api-key is missing at middleware before the handler runs.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QboErrorResponse" } } } }, "500": { "description": "QBO client ID is not configured, or an unexpected error occurred while constructing the authorization redirect.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QboErrorResponse" } } } } } } }, "/api/integrations/qbo/callback": { "get": { "summary": "Handle QuickBooks Online OAuth callback", "description": "OAuth 2.0 redirect endpoint for QuickBooks Online. Intuit redirects the browser here with code, state, and realmId after authorization. The handler decodes tenantId and csrf from the base64url state parameter, exchanges the code for access and refresh tokens, stores credentials in the tenant secret qbo_credentials keyed by realmId, and redirects back to /msp/settings with qbo_status=success or qbo_status=failure and an error code. The route returns redirects rather than JSON from the handler. Like the connect route, it is not currently listed in apiKeySkipPaths, so middleware may reject real Intuit callbacks that do not include x-api-key.", "tags": [ "Integrations - QuickBooks" ], "security": [], "extensions": { "x-oauth-provider": "quickbooks-online", "x-redirect-endpoint": true, "x-token-secret-name": "qbo_credentials", "x-middleware-api-key-required-currently": true, "x-csrf-validation-implemented": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "OAuth 2.0 authorization code returned by Intuit. Required unless Intuit sends an error parameter." }, "required": false, "description": "OAuth 2.0 authorization code returned by Intuit. Required unless Intuit sends an error parameter.", "name": "code", "in": "query" }, { "schema": { "type": "string", "description": "Base64url-encoded JSON containing tenantId and csrf generated by the connect endpoint." }, "required": false, "description": "Base64url-encoded JSON containing tenantId and csrf generated by the connect endpoint.", "name": "state", "in": "query" }, { "schema": { "type": "string", "description": "QuickBooks company/realm ID returned by Intuit." }, "required": false, "description": "QuickBooks company/realm ID returned by Intuit.", "name": "realmId", "in": "query" }, { "schema": { "type": "string", "description": "OAuth error returned by Intuit when authorization fails or is denied." }, "required": false, "description": "OAuth error returned by Intuit when authorization fails or is denied.", "name": "error", "in": "query" } ], "responses": { "307": { "description": "Redirect to the MSP settings integrations page. Success redirects with qbo_status=success; failures redirect with qbo_status=failure and an error such as qbo_error, missing_params, invalid_state, csrf_mismatch, config_error, token_exchange_failed, or callback_processing_error. The handler returns no JSON body." }, "401": { "description": "Middleware-level API-key rejection can occur before the OAuth callback handler runs.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QboErrorResponse" } } } } } } }, "/api/v1/integrations/quickbooks/accounts": { "get": { "summary": "Get QuickBooks chart of accounts", "description": "Returns QuickBooks accounts list; current implementation is a stub empty array. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getAccounts). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getAccounts()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/accounts", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "entity_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "force_refresh", "in": "query" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/accounts/mappings": { "get": { "summary": "List account mappings", "description": "Returns paginated account mapping configuration; current implementation is stubbed. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getAccountMappings). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getAccountMappings()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/accounts/mappings", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Paginated records returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } }, "put": { "summary": "Configure account mappings", "description": "Stores/updates account mapping configuration for integration export/import. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (configureAccountMappings). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.configureAccountMappings()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/accounts/mappings", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1AccountMappingsBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/connection/refresh": { "post": { "summary": "Refresh QuickBooks connection tokens", "description": "Refresh endpoint currently returns synthetic success payload (service TODO). Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (refreshConnection). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.refreshConnection()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/connection/refresh", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/connection/status": { "get": { "summary": "Get QuickBooks connection status", "description": "Returns tenant connection status and quick links for integration actions. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getConnectionStatus). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getConnectionStatus()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/connection/status", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/connection/test": { "post": { "summary": "Run QuickBooks connection test", "description": "Runs connection diagnostics against configured QuickBooks tenant. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (testConnection). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.testConnection()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/connection/test", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ConnectionTestBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/customers/mappings": { "get": { "summary": "List customer mappings", "description": "Returns paginated customer mapping records. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getCustomerMappings). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getCustomerMappings()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/customers/mappings", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "entity_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "force_refresh", "in": "query" } ], "responses": { "200": { "description": "Paginated records returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/customers/mappings/{mapping_id}": { "delete": { "summary": "Delete customer mapping", "description": "Delete handler currently returns 204 stub response (service TODO). Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (deleteCustomerMapping). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.deleteCustomerMapping()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/customers/mappings/{mapping_id}", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Mapping identifier extracted from path segment mapping_id." }, "required": true, "description": "Mapping identifier extracted from path segment mapping_id.", "name": "mapping_id", "in": "path" } ], "responses": { "204": { "description": "Operation completed with no response body." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/customers/sync": { "post": { "summary": "Sync customers", "description": "Synchronizes customer entities between Alga and QuickBooks. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (syncCustomers). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.syncCustomers()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/customers/sync", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1CustomerSyncBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/diagnostics": { "post": { "summary": "Run QuickBooks diagnostics", "description": "Runs diagnostic checks; current implementation returns synthetic health data. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (runDiagnostics). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "admin", "x-controller-method": "ApiQuickBooksController.runDiagnostics()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/diagnostics", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/health": { "get": { "summary": "Get integration health", "description": "Returns integration health summary from QuickBooksService. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getHealth). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getHealth()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/health", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/health/config": { "get": { "summary": "Get health monitoring config", "description": "Returns health monitoring configuration; current implementation is stubbed. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getHealthConfig). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getHealthConfig()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/health/config", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } }, "put": { "summary": "Update health monitoring config", "description": "Updates health monitoring configuration (controller-side synthetic response). Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (updateHealthConfig). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "admin", "x-controller-method": "ApiQuickBooksController.updateHealthConfig()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/health/config", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1HealthConfigBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/invoices/export": { "post": { "summary": "Export invoices to QuickBooks", "description": "Exports selected invoices to QuickBooks and returns job/result metadata. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (exportInvoices). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.exportInvoices()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/invoices/export", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1InvoiceExportBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/invoices/import": { "post": { "summary": "Import invoices from QuickBooks", "description": "Imports QuickBooks invoices into Alga with optional upsert behavior. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (importInvoices). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.importInvoices()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/invoices/import", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1InvoiceImportBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/items": { "get": { "summary": "List QuickBooks items", "description": "Returns items/services catalog; current implementation returns sample stub rows. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getItems). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getItems()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/items", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "entity_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "force_refresh", "in": "query" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/mappings": { "get": { "summary": "List data mappings", "description": "Returns paginated list of generic QuickBooks data mapping configurations. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getDataMappings). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getDataMappings()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/mappings", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "entity_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "force_refresh", "in": "query" } ], "responses": { "200": { "description": "Paginated records returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } }, "post": { "summary": "Create data mapping", "description": "Creates one data mapping configuration; current implementation returns synthetic mapping id. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (createDataMapping). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.createDataMapping()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/mappings", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1DataMappingBody" } } } }, "responses": { "201": { "description": "Mapping created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/mappings/{mapping_id}": { "get": { "summary": "Get data mapping by id", "description": "Returns one data mapping record by mapping_id. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getDataMappingById). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getDataMappingById()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/mappings/{mapping_id}", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Mapping identifier extracted from path segment mapping_id." }, "required": true, "description": "Mapping identifier extracted from path segment mapping_id.", "name": "mapping_id", "in": "path" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "404": { "description": "Requested mapping/sync record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } }, "put": { "summary": "Update data mapping", "description": "Updates one data mapping configuration by mapping_id. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (updateDataMapping). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.updateDataMapping()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/mappings/{mapping_id}", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Mapping identifier extracted from path segment mapping_id." }, "required": true, "description": "Mapping identifier extracted from path segment mapping_id.", "name": "mapping_id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1DataMappingBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } }, "delete": { "summary": "Delete data mapping", "description": "Deletes one data mapping configuration (current controller returns 204 stub). Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (deleteDataMapping). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.deleteDataMapping()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/mappings/{mapping_id}", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Mapping identifier extracted from path segment mapping_id." }, "required": true, "description": "Mapping identifier extracted from path segment mapping_id.", "name": "mapping_id", "in": "path" } ], "responses": { "204": { "description": "Operation completed with no response body." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/oauth/callback": { "post": { "summary": "Handle QuickBooks OAuth callback payload", "description": "Consumes OAuth callback payload with code/state/realm and persists connection tokens. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (handleOAuthCallback). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.handleOAuthCallback()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/oauth/callback", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1OAuthCallbackBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/oauth/disconnect": { "delete": { "summary": "Disconnect QuickBooks OAuth", "description": "Revokes/disconnects QuickBooks tenant credentials and returns 204. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (disconnectOAuth). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "admin", "x-controller-method": "ApiQuickBooksController.disconnectOAuth()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/oauth/disconnect", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "204": { "description": "Operation completed with no response body." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/oauth/initiate": { "post": { "summary": "Initiate QuickBooks OAuth flow", "description": "Generates authorization URL for QuickBooks OAuth with tenant-scoped state. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (initiateOAuth). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.initiateOAuth()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/oauth/initiate", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1OAuthInitiateBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/payment-methods": { "get": { "summary": "List QuickBooks payment methods", "description": "Returns payment methods; current implementation returns sample stub rows. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getPaymentMethods). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getPaymentMethods()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/payment-methods", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "entity_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "force_refresh", "in": "query" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/payments/sync": { "post": { "summary": "Sync payments", "description": "Synchronizes payment records between Alga and QuickBooks. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (syncPayments). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.syncPayments()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/payments/sync", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1PaymentSyncBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/sync/bulk": { "post": { "summary": "Run bulk QuickBooks sync", "description": "Starts multi-operation bulk sync and returns accepted job payload. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (bulkSync). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.bulkSync()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/sync/bulk", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1BulkSyncBody" } } } }, "responses": { "202": { "description": "Async sync job accepted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/sync/full": { "post": { "summary": "Run full QuickBooks sync", "description": "Starts full synchronization workflow and returns accepted job payload. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (fullSync). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "admin", "x-controller-method": "ApiQuickBooksController.fullSync()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/sync/full", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "202": { "description": "Async sync job accepted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/sync/history": { "get": { "summary": "List sync history", "description": "Returns paginated synchronization history with status/type/date filters. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getSyncHistory). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getSyncHistory()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/sync/history", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "pending", "in_progress", "completed", "failed", "cancelled", "partial" ] }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string", "enum": [ "customer_sync", "invoice_export", "invoice_import", "payment_sync", "item_sync", "tax_sync", "full_sync", "test_connection" ] }, "required": false, "name": "operation_type", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_to", "in": "query" } ], "responses": { "200": { "description": "Paginated records returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/sync/status": { "get": { "summary": "Get current sync status", "description": "Returns current sync status summary (currently stub status payload). Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getSyncStatus). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getSyncStatus()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/sync/status", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/sync/status/{sync_id}": { "get": { "summary": "Get sync status by id", "description": "Returns detailed status for one sync operation id. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getSyncStatusById). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getSyncStatusById()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/sync/status/{sync_id}", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Sync operation identifier extracted from path segment sync_id." }, "required": true, "description": "Sync operation identifier extracted from path segment sync_id.", "name": "sync_id", "in": "path" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "404": { "description": "Requested mapping/sync record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/sync/{sync_id}/cancel": { "post": { "summary": "Cancel sync operation", "description": "Cancel endpoint currently returns 204 stub response (service TODO). Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (cancelSync). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.cancelSync()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/sync/{sync_id}/cancel", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Sync operation identifier extracted from path segment sync_id." }, "required": true, "description": "Sync operation identifier extracted from path segment sync_id.", "name": "sync_id", "in": "path" } ], "responses": { "204": { "description": "Operation completed with no response body." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/sync/{sync_id}/retry": { "post": { "summary": "Retry sync operation", "description": "Retries a failed sync and returns synthetic new retry sync id. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (retrySync). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.retrySync()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/sync/{sync_id}/retry", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Sync operation identifier extracted from path segment sync_id." }, "required": true, "description": "Sync operation identifier extracted from path segment sync_id.", "name": "sync_id", "in": "path" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/tax-codes": { "get": { "summary": "List QuickBooks tax codes", "description": "Returns tax code list; current implementation is a stub empty array. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getTaxCodes). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getTaxCodes()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/tax-codes", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "entity_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "force_refresh", "in": "query" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/tax-codes/mappings": { "get": { "summary": "List tax mappings", "description": "Returns paginated tax mapping configuration; current implementation is stubbed. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getTaxMappings). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getTaxMappings()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/tax-codes/mappings", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Paginated records returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } }, "put": { "summary": "Configure tax mappings", "description": "Stores/updates mapping from Alga tax regions to QuickBooks tax codes. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (configureTaxMappings). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.configureTaxMappings()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/tax-codes/mappings", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1TaxMappingsBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/integrations/quickbooks/terms": { "get": { "summary": "List QuickBooks terms", "description": "Returns payment terms catalog; current implementation returns sample stub rows. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getTerms). This family uses route handlers that instantiate ApiQuickBooksController and usually wrap calls in explicit try/catch handleApiError blocks.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getTerms()", "x-quickbooks-route-family": "integrations", "x-quickbooks-alias-path": "/terms", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "entity_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "force_refresh", "in": "query" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/accounts": { "get": { "summary": "Get QuickBooks chart of accounts", "description": "Returns QuickBooks accounts list; current implementation is a stub empty array. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getAccounts). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getAccounts()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/accounts", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "entity_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "force_refresh", "in": "query" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/accounts/mappings": { "get": { "summary": "List account mappings", "description": "Returns paginated account mapping configuration; current implementation is stubbed. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getAccountMappings). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getAccountMappings()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/accounts/mappings", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Paginated records returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } }, "put": { "summary": "Configure account mappings", "description": "Stores/updates account mapping configuration for integration export/import. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (configureAccountMappings). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.configureAccountMappings()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/accounts/mappings", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1AccountMappingsBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/connection/refresh": { "post": { "summary": "Refresh QuickBooks connection tokens", "description": "Refresh endpoint currently returns synthetic success payload (service TODO). Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (refreshConnection). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.refreshConnection()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/connection/refresh", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/connection/status": { "get": { "summary": "Get QuickBooks connection status", "description": "Returns tenant connection status and quick links for integration actions. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getConnectionStatus). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getConnectionStatus()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/connection/status", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/connection/test": { "post": { "summary": "Run QuickBooks connection test", "description": "Runs connection diagnostics against configured QuickBooks tenant. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (testConnection). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.testConnection()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/connection/test", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ConnectionTestBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/customers/mappings": { "get": { "summary": "List customer mappings", "description": "Returns paginated customer mapping records. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getCustomerMappings). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getCustomerMappings()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/customers/mappings", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "entity_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "force_refresh", "in": "query" } ], "responses": { "200": { "description": "Paginated records returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/customers/mappings/{mapping_id}": { "delete": { "summary": "Delete customer mapping", "description": "Delete handler currently returns 204 stub response (service TODO). Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (deleteCustomerMapping). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.deleteCustomerMapping()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/customers/mappings/{mapping_id}", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Mapping identifier extracted from path segment mapping_id." }, "required": true, "description": "Mapping identifier extracted from path segment mapping_id.", "name": "mapping_id", "in": "path" } ], "responses": { "204": { "description": "Operation completed with no response body." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/customers/sync": { "post": { "summary": "Sync customers", "description": "Synchronizes customer entities between Alga and QuickBooks. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (syncCustomers). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.syncCustomers()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/customers/sync", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1CustomerSyncBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/diagnostics": { "post": { "summary": "Run QuickBooks diagnostics", "description": "Runs diagnostic checks; current implementation returns synthetic health data. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (runDiagnostics). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "admin", "x-controller-method": "ApiQuickBooksController.runDiagnostics()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/diagnostics", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/health": { "get": { "summary": "Get integration health", "description": "Returns integration health summary from QuickBooksService. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getHealth). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getHealth()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/health", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/health/config": { "get": { "summary": "Get health monitoring config", "description": "Returns health monitoring configuration; current implementation is stubbed. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getHealthConfig). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getHealthConfig()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/health/config", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } }, "put": { "summary": "Update health monitoring config", "description": "Updates health monitoring configuration (controller-side synthetic response). Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (updateHealthConfig). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "admin", "x-controller-method": "ApiQuickBooksController.updateHealthConfig()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/health/config", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1HealthConfigBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/invoices/export": { "post": { "summary": "Export invoices to QuickBooks", "description": "Exports selected invoices to QuickBooks and returns job/result metadata. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (exportInvoices). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.exportInvoices()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/invoices/export", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1InvoiceExportBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/invoices/import": { "post": { "summary": "Import invoices from QuickBooks", "description": "Imports QuickBooks invoices into Alga with optional upsert behavior. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (importInvoices). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.importInvoices()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/invoices/import", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1InvoiceImportBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/items": { "get": { "summary": "List QuickBooks items", "description": "Returns items/services catalog; current implementation returns sample stub rows. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getItems). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getItems()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/items", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "entity_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "force_refresh", "in": "query" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/mappings": { "get": { "summary": "List data mappings", "description": "Returns paginated list of generic QuickBooks data mapping configurations. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getDataMappings). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getDataMappings()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/mappings", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "entity_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "force_refresh", "in": "query" } ], "responses": { "200": { "description": "Paginated records returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } }, "post": { "summary": "Create data mapping", "description": "Creates one data mapping configuration; current implementation returns synthetic mapping id. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (createDataMapping). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.createDataMapping()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/mappings", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1DataMappingBody" } } } }, "responses": { "201": { "description": "Mapping created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/mappings/{mapping_id}": { "get": { "summary": "Get data mapping by id", "description": "Returns one data mapping record by mapping_id. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getDataMappingById). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getDataMappingById()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/mappings/{mapping_id}", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Mapping identifier extracted from path segment mapping_id." }, "required": true, "description": "Mapping identifier extracted from path segment mapping_id.", "name": "mapping_id", "in": "path" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "404": { "description": "Requested mapping/sync record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } }, "put": { "summary": "Update data mapping", "description": "Updates one data mapping configuration by mapping_id. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (updateDataMapping). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.updateDataMapping()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/mappings/{mapping_id}", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Mapping identifier extracted from path segment mapping_id." }, "required": true, "description": "Mapping identifier extracted from path segment mapping_id.", "name": "mapping_id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1DataMappingBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } }, "delete": { "summary": "Delete data mapping", "description": "Deletes one data mapping configuration (current controller returns 204 stub). Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (deleteDataMapping). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.deleteDataMapping()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/mappings/{mapping_id}", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Mapping identifier extracted from path segment mapping_id." }, "required": true, "description": "Mapping identifier extracted from path segment mapping_id.", "name": "mapping_id", "in": "path" } ], "responses": { "204": { "description": "Operation completed with no response body." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/oauth/callback": { "post": { "summary": "Handle QuickBooks OAuth callback payload", "description": "Consumes OAuth callback payload with code/state/realm and persists connection tokens. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (handleOAuthCallback). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.handleOAuthCallback()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/oauth/callback", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1OAuthCallbackBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/oauth/disconnect": { "delete": { "summary": "Disconnect QuickBooks OAuth", "description": "Revokes/disconnects QuickBooks tenant credentials and returns 204. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (disconnectOAuth). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "admin", "x-controller-method": "ApiQuickBooksController.disconnectOAuth()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/oauth/disconnect", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "204": { "description": "Operation completed with no response body." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/oauth/initiate": { "post": { "summary": "Initiate QuickBooks OAuth flow", "description": "Generates authorization URL for QuickBooks OAuth with tenant-scoped state. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (initiateOAuth). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.initiateOAuth()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/oauth/initiate", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1OAuthInitiateBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/payment-methods": { "get": { "summary": "List QuickBooks payment methods", "description": "Returns payment methods; current implementation returns sample stub rows. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getPaymentMethods). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getPaymentMethods()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/payment-methods", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "entity_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "force_refresh", "in": "query" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/payments/sync": { "post": { "summary": "Sync payments", "description": "Synchronizes payment records between Alga and QuickBooks. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (syncPayments). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.syncPayments()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/payments/sync", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1PaymentSyncBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/sync/bulk": { "post": { "summary": "Run bulk QuickBooks sync", "description": "Starts multi-operation bulk sync and returns accepted job payload. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (bulkSync). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.bulkSync()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/sync/bulk", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1BulkSyncBody" } } } }, "responses": { "202": { "description": "Async sync job accepted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/sync/full": { "post": { "summary": "Run full QuickBooks sync", "description": "Starts full synchronization workflow and returns accepted job payload. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (fullSync). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "admin", "x-controller-method": "ApiQuickBooksController.fullSync()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/sync/full", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "202": { "description": "Async sync job accepted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/sync/history": { "get": { "summary": "List sync history", "description": "Returns paginated synchronization history with status/type/date filters. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getSyncHistory). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getSyncHistory()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/sync/history", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "pending", "in_progress", "completed", "failed", "cancelled", "partial" ] }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string", "enum": [ "customer_sync", "invoice_export", "invoice_import", "payment_sync", "item_sync", "tax_sync", "full_sync", "test_connection" ] }, "required": false, "name": "operation_type", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "date_to", "in": "query" } ], "responses": { "200": { "description": "Paginated records returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/sync/status": { "get": { "summary": "Get current sync status", "description": "Returns current sync status summary (currently stub status payload). Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getSyncStatus). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getSyncStatus()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/sync/status", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/sync/status/{sync_id}": { "get": { "summary": "Get sync status by id", "description": "Returns detailed status for one sync operation id. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getSyncStatusById). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getSyncStatusById()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/sync/status/{sync_id}", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Sync operation identifier extracted from path segment sync_id." }, "required": true, "description": "Sync operation identifier extracted from path segment sync_id.", "name": "sync_id", "in": "path" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "404": { "description": "Requested mapping/sync record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/sync/{sync_id}/cancel": { "post": { "summary": "Cancel sync operation", "description": "Cancel endpoint currently returns 204 stub response (service TODO). Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (cancelSync). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.cancelSync()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/sync/{sync_id}/cancel", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Sync operation identifier extracted from path segment sync_id." }, "required": true, "description": "Sync operation identifier extracted from path segment sync_id.", "name": "sync_id", "in": "path" } ], "responses": { "204": { "description": "Operation completed with no response body." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/sync/{sync_id}/retry": { "post": { "summary": "Retry sync operation", "description": "Retries a failed sync and returns synthetic new retry sync id. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (retrySync). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.retrySync()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/sync/{sync_id}/retry", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Sync operation identifier extracted from path segment sync_id." }, "required": true, "description": "Sync operation identifier extracted from path segment sync_id.", "name": "sync_id", "in": "path" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/tax-codes": { "get": { "summary": "List QuickBooks tax codes", "description": "Returns tax code list; current implementation is a stub empty array. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getTaxCodes). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getTaxCodes()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/tax-codes", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "entity_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "force_refresh", "in": "query" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/tax-codes/mappings": { "get": { "summary": "List tax mappings", "description": "Returns paginated tax mapping configuration; current implementation is stubbed. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getTaxMappings). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getTaxMappings()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/tax-codes/mappings", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Paginated records returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } }, "put": { "summary": "Configure tax mappings", "description": "Stores/updates mapping from Alga tax regions to QuickBooks tax codes. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (configureTaxMappings). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "write", "x-controller-method": "ApiQuickBooksController.configureTaxMappings()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/tax-codes/mappings", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1TaxMappingsBody" } } } }, "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/quickbooks/terms": { "get": { "summary": "List QuickBooks terms", "description": "Returns payment terms catalog; current implementation returns sample stub rows. Both /api/v1/integrations/quickbooks/* and /api/v1/quickbooks/* map to the same ApiQuickBooksController method (getTerms). This family is a path alias that binds controller methods directly (for example `export const GET = controller.getAccounts()`) and relies on controller-level error handling.", "tags": [ "QuickBooks v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated by ApiQuickBooksController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quickbooks", "x-rbac-action": "read", "x-controller-method": "ApiQuickBooksController.getTerms()", "x-quickbooks-route-family": "quickbooks", "x-quickbooks-alias-path": "/terms", "x-quickbooks-family-alias": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "entity_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "force_refresh", "in": "query" } ], "responses": { "200": { "description": "QuickBooks operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "403": { "description": "QuickBooks RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } }, "500": { "description": "Unexpected QuickBooks endpoint failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuickBooksV1ApiError" } } } } } } }, "/api/v1/boards": { "get": { "summary": "List boards", "description": "Returns a paginated list of ticket boards for the current tenant. Use this to discover valid board_id values before creating tickets or statuses.", "tags": [ "Boards" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket", "x-chat-callable": true, "x-chat-display-name": "List Boards", "x-chat-rbac-resource": "ticket", "x-chat-approval-required": false }, "x-alga-products": [ "psa", "algadesk" ], "responses": { "200": { "description": "Boards returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BoardListEnvelope" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "Create board", "description": "Creates a new ticket board. After creating a board, create at least one status for it via POST /api/v1/statuses so tickets can be assigned to the board.", "tags": [ "Boards" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket", "x-chat-callable": true, "x-chat-display-name": "Create Board", "x-chat-rbac-resource": "ticket", "x-chat-approval-required": true }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "description": "Board creation payload.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BoardCreateRequest" }, "description": "Board creation payload." } } }, "responses": { "201": { "description": "Board created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BoardEnvelope" } } } }, "400": { "description": "Validation error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/boards/{id}": { "get": { "summary": "Get board by ID", "description": "Returns a single board by its UUID.", "tags": [ "Boards" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket", "x-chat-callable": true, "x-chat-display-name": "Get Board", "x-chat-rbac-resource": "ticket", "x-chat-approval-required": false }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Board UUID." }, "required": true, "description": "Board UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Board returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BoardEnvelope" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Board not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "put": { "summary": "Update board", "description": "Updates an existing board. All fields are optional — only send the fields you want to change.", "tags": [ "Boards" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket", "x-chat-callable": true, "x-chat-display-name": "Update Board", "x-chat-rbac-resource": "ticket", "x-chat-approval-required": true }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Board UUID." }, "required": true, "description": "Board UUID.", "name": "id", "in": "path" } ], "requestBody": { "description": "Board update payload.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BoardUpdateRequest" }, "description": "Board update payload." } } }, "responses": { "200": { "description": "Board updated successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BoardEnvelope" } } } }, "400": { "description": "Validation error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Board not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "delete": { "summary": "Delete board", "description": "Deletes a board by its UUID. This will fail if the board has associated tickets.", "tags": [ "Boards" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket", "x-chat-callable": true, "x-chat-display-name": "Delete Board", "x-chat-rbac-resource": "ticket", "x-chat-approval-required": true }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Board UUID." }, "required": true, "description": "Board UUID.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Board deleted successfully." }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Board not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/statuses": { "get": { "summary": "List statuses", "description": "Returns a paginated list of statuses. For ticket statuses, filter by type=ticket and board_id to get statuses that belong to a specific board. The status_id must belong to the same board_id when creating tickets.", "tags": [ "Statuses" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket", "x-chat-callable": true, "x-chat-display-name": "List Statuses", "x-chat-rbac-resource": "ticket", "x-chat-approval-required": false }, "x-alga-products": [ "psa", "algadesk" ], "responses": { "200": { "description": "Statuses returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusListEnvelope" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "Create status", "description": "Creates a new status. For ticket statuses, board_id is required. The status_type must be one of: ticket, project, project_task, interaction.", "tags": [ "Statuses" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket", "x-chat-callable": true, "x-chat-display-name": "Create Status", "x-chat-rbac-resource": "ticket", "x-chat-approval-required": true }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "description": "Status creation payload.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusCreateRequest" }, "description": "Status creation payload." } } }, "responses": { "201": { "description": "Status created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusEnvelope" } } } }, "400": { "description": "Validation error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/statuses/{id}": { "get": { "summary": "Get status by ID", "description": "Returns a single status by its UUID.", "tags": [ "Statuses" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket", "x-chat-callable": true, "x-chat-display-name": "Get Status", "x-chat-rbac-resource": "ticket", "x-chat-approval-required": false }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Status UUID." }, "required": true, "description": "Status UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Status returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusEnvelope" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Status not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "put": { "summary": "Update status", "description": "Updates an existing status. All fields are optional — only send the fields you want to change.", "tags": [ "Statuses" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket", "x-chat-callable": true, "x-chat-display-name": "Update Status", "x-chat-rbac-resource": "ticket", "x-chat-approval-required": true }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Status UUID." }, "required": true, "description": "Status UUID.", "name": "id", "in": "path" } ], "requestBody": { "description": "Status update payload.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusUpdateRequest" }, "description": "Status update payload." } } }, "responses": { "200": { "description": "Status updated successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusEnvelope" } } } }, "400": { "description": "Validation error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Status not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "delete": { "summary": "Delete status", "description": "Deletes a status by its UUID. Cannot delete the last default status for a board.", "tags": [ "Statuses" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket", "x-chat-callable": true, "x-chat-display-name": "Delete Status", "x-chat-rbac-resource": "ticket", "x-chat-approval-required": true }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Status UUID." }, "required": true, "description": "Status UUID.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Status deleted successfully." }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Status not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/search": { "get": { "summary": "Unified full-text search", "description": "Full-text search across all indexed business records (tickets, clients, contacts, projects, assets, invoices, contracts, documents, knowledge-base articles, and more) backed by the shared app_search_index. Results are tenant-scoped and filtered by the API key user's permissions: a coarse per-type permission gate plus a per-row access-control check, so only records the user could see in-app are returned. No dedicated search permission is required — any valid API key may call this endpoint. Client-portal API keys are automatically scoped to their own client.", "tags": [ "Search" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-acl-filtered": true, "x-not-paginated": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "minLength": 1, "maxLength": 200, "description": "Full-text query. A concise expression of likely record words, identifiers, names, or quoted phrases — not a full sentence. Supports OR for alternatives (e.g. \"laptop OR workstation\")." }, "required": true, "description": "Full-text query. A concise expression of likely record words, identifiers, names, or quoted phrases — not a full sentence. Supports OR for alternatives (e.g. \"laptop OR workstation\").", "name": "query", "in": "query" }, { "schema": { "type": "string", "description": "Comma-separated list of object types to restrict the search (e.g. \"ticket,project\"). Omit to search every type the API key's user is permitted to read. Allowed values: client, contact, user, ticket, ticket_comment, project, project_phase, project_task, project_task_comment, asset, invoice, invoice_item, invoice_annotation, contract, client_contract, document, kb_article, service_catalog, service_request_submission, service_request_definition, workflow_task, interaction, schedule_entry, time_entry, board, category, tag, status." }, "required": false, "description": "Comma-separated list of object types to restrict the search (e.g. \"ticket,project\"). Omit to search every type the API key's user is permitted to read. Allowed values: client, contact, user, ticket, ticket_comment, project, project_phase, project_task, project_task_comment, asset, invoice, invoice_item, invoice_annotation, contract, client_contract, document, kb_article, service_catalog, service_request_submission, service_request_definition, workflow_task, interaction, schedule_entry, time_entry, board, category, tag, status.", "name": "types", "in": "query" }, { "schema": { "type": "integer", "minimum": 1, "maximum": 100, "description": "Maximum number of results to return. Defaults to 30; capped at 100." }, "required": false, "description": "Maximum number of results to return. Defaults to 30; capped at 100.", "name": "limit", "in": "query" }, { "schema": { "type": "string", "description": "Opaque pagination cursor copied from a prior response's nextCursor." }, "required": false, "description": "Opaque pagination cursor copied from a prior response's nextCursor.", "name": "cursor", "in": "query" }, { "schema": { "type": "string", "enum": [ "relevance", "recent" ], "description": "Result ordering. \"relevance\" (default) ranks by full-text score; \"recent\" orders by last update." }, "required": false, "description": "Result ordering. \"relevance\" (default) ranks by full-text score; \"recent\" orders by last update.", "name": "sort", "in": "query" } ], "responses": { "200": { "description": "Matching records returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchResponse" } } } }, "400": { "description": "Query parameter validation failed (missing/empty query, unknown type, or out-of-range limit).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchApiErrorEnvelope" } } } }, "401": { "description": "API key is missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchApiErrorEnvelope" } } } }, "429": { "description": "API rate limit exceeded for the key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchApiErrorEnvelope" } } } }, "500": { "description": "Unexpected error while executing the search.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchApiErrorEnvelope" } } } } } } }, "/api/v1/categories/service": { "get": { "summary": "List service categories", "description": "Returns a paginated list of service categories within the current tenant.", "tags": [ "Service Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "service-category", "x-chat-callable": true, "x-chat-display-name": "List Service Categories", "x-chat-rbac-resource": "service-category", "x-chat-approval-required": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "boolean" }, "required": false, "name": "is_active", "in": "query" }, { "schema": { "type": "integer", "minimum": 1 }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "integer", "minimum": 1, "maximum": 100 }, "required": false, "name": "limit", "in": "query" } ], "responses": { "200": { "description": "A paginated list of service categories.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedServiceCategoryResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "Create service category", "description": "Creates a new service category for the tenant. Requires the billing settings create permission.", "tags": [ "Service Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "service-category", "x-chat-callable": true, "x-chat-display-name": "Create Service Category", "x-chat-rbac-resource": "service-category", "x-chat-approval-required": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Service category payload", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ServiceCategoryCreateRequest" }, "description": "Service category payload" } } }, "responses": { "201": { "description": "Service category created successfully.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/ServiceCategory" } }, "required": [ "data" ] } } } }, "400": { "description": "Validation error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/categories/service/{id}": { "get": { "summary": "Get service category", "description": "Returns one service category by category_id for the authenticated tenant. Requires billing_settings:read permission.", "tags": [ "Service Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "billing_settings", "x-rbac-action": "read", "x-id-provenance": { "category_id": "service_categories.category_id" } }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Category UUID from service_categories.category_id or categories.category_id." }, "required": true, "description": "Category UUID from service_categories.category_id or categories.category_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Service category returned successfully.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/ServiceCategory" } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid category id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks billing settings read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Service category not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "put": { "summary": "Update service category", "description": "Updates one service category by category_id. Requires billing_settings:update permission.", "tags": [ "Service Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "billing_settings", "x-rbac-action": "update", "x-id-provenance": { "category_id": "service_categories.category_id" } }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Category UUID from service_categories.category_id or categories.category_id." }, "required": true, "description": "Category UUID from service_categories.category_id or categories.category_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ServiceCategoryCreateRequest" } } } }, "responses": { "200": { "description": "Service category updated successfully.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/ServiceCategory" } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid category id or request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks billing settings update permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Service category not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "delete": { "summary": "Delete service category", "description": "Deletes one service category by category_id. Requires billing_settings:delete permission.", "tags": [ "Service Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "billing_settings", "x-rbac-action": "delete", "x-id-provenance": { "category_id": "service_categories.category_id" } }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Category UUID from service_categories.category_id or categories.category_id." }, "required": true, "description": "Category UUID from service_categories.category_id or categories.category_id.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Service category deleted successfully." }, "400": { "description": "Invalid category id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks billing settings delete permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Service category not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/categories/ticket": { "get": { "summary": "List ticket categories", "description": "Returns paginated ticket categories. Requires ticket_settings:read permission.", "tags": [ "Ticket Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket_settings", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "created_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "created_to", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "updated_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "updated_to", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "category_name", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "board_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "parent_category", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_parent", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_child", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "depth", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "active", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "offset", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort_by", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "sort_order", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_hierarchy", "in": "query" }, { "schema": { "type": "string", "enum": [ "service", "ticket" ] }, "required": false, "name": "category_type", "in": "query" } ], "responses": { "200": { "description": "Ticket categories returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedTicketCategoryResponse" } } } }, "400": { "description": "Invalid query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks ticket settings read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "Create ticket category", "description": "Creates a ticket category row for one board. Requires ticket_settings:create permission.", "tags": [ "Ticket Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket_settings", "x-rbac-action": "create", "x-id-provenance": { "category_id": "categories.category_id" } }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TicketCategoryCreateRequest" } } } }, "responses": { "201": { "description": "Ticket category created successfully.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/TicketCategory" } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks ticket settings create permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/categories/ticket/{id}": { "get": { "summary": "Get ticket category", "description": "Returns one ticket category by category_id. Requires ticket_settings:read permission.", "tags": [ "Ticket Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket_settings", "x-rbac-action": "read", "x-id-provenance": { "category_id": "categories.category_id" } }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Category UUID from service_categories.category_id or categories.category_id." }, "required": true, "description": "Category UUID from service_categories.category_id or categories.category_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Ticket category returned successfully.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/TicketCategory" } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid category id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks ticket settings read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Ticket category not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "put": { "summary": "Update ticket category", "description": "Updates one ticket category by category_id. Requires ticket_settings:update permission.", "tags": [ "Ticket Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket_settings", "x-rbac-action": "update", "x-id-provenance": { "category_id": "categories.category_id" } }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Category UUID from service_categories.category_id or categories.category_id." }, "required": true, "description": "Category UUID from service_categories.category_id or categories.category_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TicketCategoryUpdateRequest" } } } }, "responses": { "200": { "description": "Ticket category updated successfully.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/TicketCategory" } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid category id or request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks ticket settings update permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Ticket category not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "delete": { "summary": "Delete ticket category", "description": "Deletes one ticket category by category_id. Requires ticket_settings:delete permission.", "tags": [ "Ticket Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket_settings", "x-rbac-action": "delete", "x-id-provenance": { "category_id": "categories.category_id" } }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Category UUID from service_categories.category_id or categories.category_id." }, "required": true, "description": "Category UUID from service_categories.category_id or categories.category_id.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Ticket category deleted successfully." }, "400": { "description": "Invalid category id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks ticket settings delete permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Ticket category not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/categories/ticket/tree": { "get": { "summary": "Get ticket category tree (implicit board id)", "description": "Calls getCategoryTree and derives boardId from the last URL path segment. For this path without a board parameter, the current implementation passes literal `tree` as boardId to CategoryService.getCategoryTree.", "tags": [ "Ticket Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket_settings", "x-rbac-action": "read", "x-board-id-derived-from-last-path-segment": true, "x-no-board-param-route-currently-passes-tree-literal": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Category tree response for derived board id value.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CategoryTreeResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks ticket settings read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected category tree failure for invalid derived board id.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/categories/ticket/tree/{boardId}": { "get": { "summary": "Get ticket category tree by board", "description": "Returns hierarchical ticket categories for a board route. Current controller still derives board id from the last path segment instead of reading req.params directly; this works for this route because the last segment is boardId.", "tags": [ "Ticket Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket_settings", "x-rbac-action": "read", "x-board-id-derived-from-last-path-segment": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Board UUID used by ticket category tree queries." }, "required": true, "description": "Board UUID used by ticket category tree queries.", "name": "boardId", "in": "path" } ], "responses": { "200": { "description": "Category tree returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CategoryTreeResponse" } } } }, "400": { "description": "Invalid board id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks ticket settings read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Unexpected category tree failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/categories/ticket/move": { "post": { "summary": "Move ticket category in hierarchy", "description": "Moves a ticket category under a new parent. Requires ticket_settings:update permission.", "tags": [ "Ticket Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "ticket_settings", "x-rbac-action": "update", "x-id-provenance": { "category_id": "categories.category_id" } }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CategoryMoveRequest" } } } }, "responses": { "200": { "description": "Category moved successfully.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/TicketCategory" } }, "required": [ "data" ] } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks ticket settings update permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Category not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/categories/search": { "get": { "summary": "Search categories", "description": "Searches categories across service/ticket types. Permission resource is chosen dynamically from category_type (defaults to ticket_settings when omitted).", "tags": [ "Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource-dynamic-by-category-type": { "service": "billing_settings", "ticket_or_default": "ticket_settings" }, "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "minLength": 1 }, "required": true, "name": "search_term", "in": "query" }, { "schema": { "type": "string", "enum": [ "service", "ticket" ] }, "required": false, "name": "category_type", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "board_id", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_inactive", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "offset", "in": "query" } ], "responses": { "200": { "description": "Category search results returned.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object", "additionalProperties": {} } }, "pagination": { "type": "object", "additionalProperties": {} }, "meta": { "type": "object", "additionalProperties": {} } }, "required": [ "data", "pagination" ] } } } }, "400": { "description": "Invalid query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the derived settings read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/categories/analytics": { "get": { "summary": "Get category analytics", "description": "Returns category analytics with generation timestamp. Permission resource is chosen dynamically from category_type (defaults to ticket_settings when omitted).", "tags": [ "Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource-dynamic-by-category-type": { "service": "billing_settings", "ticket_or_default": "ticket_settings" }, "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "enum": [ "service", "ticket" ] }, "required": false, "name": "category_type", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "board_id", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "date_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "date_to", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_usage", "in": "query" } ], "responses": { "200": { "description": "Category analytics returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CategoryAnalyticsResponse" } } } }, "400": { "description": "Invalid query parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the derived settings read permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/categories/bulk/delete": { "post": { "summary": "Bulk delete categories", "description": "Deletes multiple category ids. Permission resource is chosen dynamically from category_type; response includes success/failed counts and per-item errors.", "tags": [ "Categories" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource-dynamic-by-category-type": { "service": "billing_settings", "ticket": "ticket_settings" }, "x-rbac-action": "delete", "x-partial-failures-in-response": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkDeleteCategoriesRequest" } } } }, "responses": { "200": { "description": "Bulk deletion completed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkDeleteCategoriesResponse" } } } }, "400": { "description": "Invalid request payload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the derived settings delete permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/services": { "get": { "summary": "List services", "description": "Returns service catalog entries where item_kind is service. Use this endpoint to inspect existing service catalog records and to gather prerequisite IDs such as custom_service_type_id and category_id before creating a new service.", "tags": [ "Services", "Service Catalog" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "service", "x-chat-callable": true, "x-chat-display-name": "List Services", "x-chat-rbac-resource": "service", "x-chat-approval-required": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "integer", "minimum": 1, "default": 1 }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "service_name", "billing_method", "default_rate" ], "default": "service_name" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ], "default": "asc" }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string", "enum": [ "service", "product", "any" ] }, "required": false, "name": "item_kind", "in": "query" }, { "schema": { "anyOf": [ { "type": "string", "enum": [ "true" ] }, { "type": "string", "enum": [ "false" ] } ] }, "required": false, "name": "is_active", "in": "query" }, { "schema": { "type": "string", "enum": [ "fixed", "hourly", "usage" ] }, "required": false, "name": "billing_method", "in": "query" }, { "schema": { "anyOf": [ { "type": "string", "format": "uuid" }, { "type": "string", "enum": [ "null" ] } ] }, "required": false, "name": "category_id", "in": "query" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "custom_service_type_id", "in": "query" } ], "responses": { "200": { "description": "Services returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedServiceEnvelope" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "Create service", "description": "Creates a new service catalog entry. Resolve custom_service_type_id with GET /api/v1/service-types and category_id with GET /api/v1/categories/service before calling this endpoint. Use this endpoint for fixed, hourly, or usage-based service offerings.", "tags": [ "Services", "Service Catalog" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "service", "x-chat-callable": true, "x-chat-display-name": "Create Service", "x-chat-rbac-resource": "service", "x-chat-approval-required": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Service creation payload.", "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "service_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "custom_service_type_id": { "type": "string", "format": "uuid" }, "billing_method": { "type": "string", "enum": [ "fixed", "hourly", "usage" ] }, "default_rate": { "type": "number", "minimum": 0 }, "unit_of_measure": { "type": "string", "minLength": 1, "maxLength": 128 }, "category_id": { "anyOf": [ { "type": "string", "format": "uuid" }, { "type": "null" }, { "type": "null" } ] }, "tax_rate_id": { "anyOf": [ { "type": "string", "format": "uuid" }, { "type": "null" }, { "type": "null" } ] }, "description": { "anyOf": [ { "type": "string", "maxLength": 2048 }, { "type": "null" }, { "type": "null" } ] } }, "required": [ "service_name", "custom_service_type_id", "billing_method", "default_rate", "unit_of_measure" ] }, "description": "Service creation payload." } } }, "responses": { "201": { "description": "Service created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ServiceEnvelope" } } } }, "400": { "description": "Validation error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/services/{id}": { "get": { "summary": "Get service", "description": "Returns a single service catalog entry by UUID.", "tags": [ "Services", "Service Catalog" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "service", "x-chat-callable": true, "x-chat-display-name": "Get Service", "x-chat-rbac-resource": "service", "x-chat-approval-required": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Service UUID." }, "required": true, "description": "Service UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Service returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ServiceEnvelope" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Service not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "put": { "summary": "Update service", "description": "Updates a service catalog entry by UUID.", "tags": [ "Services", "Service Catalog" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "service", "x-chat-callable": true, "x-chat-display-name": "Update Service", "x-chat-rbac-resource": "service", "x-chat-approval-required": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Service UUID." }, "required": true, "description": "Service UUID.", "name": "id", "in": "path" } ], "requestBody": { "description": "Service update payload.", "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "service_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "custom_service_type_id": { "type": "string", "format": "uuid" }, "billing_method": { "type": "string", "enum": [ "fixed", "hourly", "usage" ] }, "default_rate": { "type": "number", "minimum": 0 }, "unit_of_measure": { "type": "string", "minLength": 1, "maxLength": 128 }, "category_id": { "anyOf": [ { "type": "string", "format": "uuid" }, { "type": "null" }, { "type": "null" } ] }, "tax_rate_id": { "anyOf": [ { "type": "string", "format": "uuid" }, { "type": "null" }, { "type": "null" } ] }, "description": { "anyOf": [ { "type": "string", "maxLength": 2048 }, { "type": "null" }, { "type": "null" } ] } } }, "description": "Service update payload." } } }, "responses": { "200": { "description": "Service updated successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ServiceEnvelope" } } } }, "400": { "description": "Validation error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Service not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "delete": { "summary": "Delete service", "description": "Deletes a service catalog entry by UUID.", "tags": [ "Services", "Service Catalog" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "service", "x-chat-callable": true, "x-chat-display-name": "Delete Service", "x-chat-rbac-resource": "service", "x-chat-approval-required": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Service UUID." }, "required": true, "description": "Service UUID.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Service deleted successfully." }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Service not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/products": { "get": { "summary": "List products", "description": "Returns product catalog entries where item_kind is product. Use this endpoint to inspect existing product catalog records and to gather prerequisite IDs such as custom_service_type_id and category_id before creating a new product.", "tags": [ "Products", "Service Catalog" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "service", "x-chat-callable": true, "x-chat-display-name": "List Products", "x-chat-rbac-resource": "service", "x-chat-approval-required": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "integer", "minimum": 1, "default": 1 }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "service_name", "default_rate" ], "default": "service_name" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ], "default": "asc" }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "anyOf": [ { "type": "string", "enum": [ "true" ] }, { "type": "string", "enum": [ "false" ] } ] }, "required": false, "name": "is_active", "in": "query" }, { "schema": { "anyOf": [ { "type": "string", "format": "uuid" }, { "type": "string", "enum": [ "null" ] } ] }, "required": false, "name": "category_id", "in": "query" }, { "schema": { "anyOf": [ { "type": "string", "enum": [ "true" ] }, { "type": "string", "enum": [ "false" ] } ] }, "required": false, "name": "is_license", "in": "query" } ], "responses": { "200": { "description": "Products returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedProductEnvelope" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "Create product", "description": "Creates a new product catalog entry. Resolve custom_service_type_id with GET /api/v1/service-types and category_id with GET /api/v1/categories/service before calling this endpoint. Products are catalog entries with item_kind product and always use billing_method usage.", "tags": [ "Products", "Service Catalog" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "service", "x-chat-callable": true, "x-chat-display-name": "Create Product", "x-chat-rbac-resource": "service", "x-chat-approval-required": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Product creation payload.", "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "service_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "custom_service_type_id": { "type": "string", "format": "uuid" }, "billing_method": { "type": "string", "enum": [ "usage" ], "default": "usage" }, "default_rate": { "type": "number", "minimum": 0, "default": 0 }, "currency_code": { "type": "string", "minLength": 3, "maxLength": 3, "default": "USD" }, "unit_of_measure": { "type": "string", "minLength": 1, "maxLength": 128 }, "category_id": { "anyOf": [ { "type": "string", "format": "uuid" }, { "type": "null" }, { "type": "null" } ] }, "tax_rate_id": { "anyOf": [ { "type": "string", "format": "uuid" }, { "type": "null" }, { "type": "null" } ] }, "description": { "anyOf": [ { "type": "string", "maxLength": 2048 }, { "type": "null" }, { "type": "null" } ] }, "sku": { "anyOf": [ { "type": "string", "maxLength": 128 }, { "type": "null" }, { "type": "null" } ] }, "cost": { "anyOf": [ { "type": "number", "minimum": 0 }, { "type": "null" }, { "type": "null" } ] }, "cost_currency": { "anyOf": [ { "type": "string", "minLength": 3, "maxLength": 3 }, { "type": "null" }, { "type": "null" } ], "default": "USD" }, "vendor": { "anyOf": [ { "type": "string", "maxLength": 255 }, { "type": "null" }, { "type": "null" } ] }, "manufacturer": { "anyOf": [ { "type": "string", "maxLength": 255 }, { "type": "null" }, { "type": "null" } ] }, "product_category": { "anyOf": [ { "type": "string", "maxLength": 255 }, { "type": "null" }, { "type": "null" } ] }, "is_license": { "type": "boolean", "default": false }, "license_term": { "anyOf": [ { "type": "string", "maxLength": 64 }, { "type": "null" }, { "type": "null" } ] }, "license_billing_cadence": { "anyOf": [ { "type": "string", "maxLength": 64 }, { "type": "null" }, { "type": "null" } ] }, "is_active": { "type": "boolean", "default": true }, "prices": { "type": "array", "items": { "type": "object", "properties": { "currency_code": { "type": "string", "minLength": 3, "maxLength": 3 }, "rate": { "type": "number", "minimum": 0 } }, "required": [ "currency_code", "rate" ] } } }, "required": [ "service_name", "custom_service_type_id", "unit_of_measure" ] }, "description": "Product creation payload." } } }, "responses": { "201": { "description": "Product created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProductEnvelope" } } } }, "400": { "description": "Validation error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/products/{id}": { "get": { "summary": "Get product", "description": "Returns a single product catalog entry by UUID.", "tags": [ "Products", "Service Catalog" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "service", "x-chat-callable": true, "x-chat-display-name": "Get Product", "x-chat-rbac-resource": "service", "x-chat-approval-required": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Product UUID." }, "required": true, "description": "Product UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Product returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProductEnvelope" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Product not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "put": { "summary": "Update product", "description": "Updates a product catalog entry by UUID.", "tags": [ "Products", "Service Catalog" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "service", "x-chat-callable": true, "x-chat-display-name": "Update Product", "x-chat-rbac-resource": "service", "x-chat-approval-required": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Product UUID." }, "required": true, "description": "Product UUID.", "name": "id", "in": "path" } ], "requestBody": { "description": "Product update payload.", "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "service_name": { "type": "string", "minLength": 1, "maxLength": 255 }, "custom_service_type_id": { "type": "string", "format": "uuid" }, "billing_method": { "type": "string", "enum": [ "usage" ], "default": "usage" }, "default_rate": { "type": "number", "minimum": 0, "default": 0 }, "currency_code": { "type": "string", "minLength": 3, "maxLength": 3, "default": "USD" }, "unit_of_measure": { "type": "string", "minLength": 1, "maxLength": 128 }, "category_id": { "anyOf": [ { "type": "string", "format": "uuid" }, { "type": "null" }, { "type": "null" } ] }, "tax_rate_id": { "anyOf": [ { "type": "string", "format": "uuid" }, { "type": "null" }, { "type": "null" } ] }, "description": { "anyOf": [ { "type": "string", "maxLength": 2048 }, { "type": "null" }, { "type": "null" } ] }, "sku": { "anyOf": [ { "type": "string", "maxLength": 128 }, { "type": "null" }, { "type": "null" } ] }, "cost": { "anyOf": [ { "type": "number", "minimum": 0 }, { "type": "null" }, { "type": "null" } ] }, "cost_currency": { "anyOf": [ { "type": "string", "minLength": 3, "maxLength": 3 }, { "type": "null" }, { "type": "null" } ], "default": "USD" }, "vendor": { "anyOf": [ { "type": "string", "maxLength": 255 }, { "type": "null" }, { "type": "null" } ] }, "manufacturer": { "anyOf": [ { "type": "string", "maxLength": 255 }, { "type": "null" }, { "type": "null" } ] }, "product_category": { "anyOf": [ { "type": "string", "maxLength": 255 }, { "type": "null" }, { "type": "null" } ] }, "is_license": { "type": "boolean", "default": false }, "license_term": { "anyOf": [ { "type": "string", "maxLength": 64 }, { "type": "null" }, { "type": "null" } ] }, "license_billing_cadence": { "anyOf": [ { "type": "string", "maxLength": 64 }, { "type": "null" }, { "type": "null" } ] }, "is_active": { "type": "boolean", "default": true }, "prices": { "type": "array", "items": { "type": "object", "properties": { "currency_code": { "type": "string", "minLength": 3, "maxLength": 3 }, "rate": { "type": "number", "minimum": 0 } }, "required": [ "currency_code", "rate" ] } } } }, "description": "Product update payload." } } }, "responses": { "200": { "description": "Product updated successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProductEnvelope" } } } }, "400": { "description": "Validation error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Product not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "delete": { "summary": "Delete product", "description": "Deletes a product catalog entry by UUID.", "tags": [ "Products", "Service Catalog" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "service", "x-chat-callable": true, "x-chat-display-name": "Delete Product", "x-chat-rbac-resource": "service", "x-chat-approval-required": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Product UUID." }, "required": true, "description": "Product UUID.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Product deleted successfully." }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Product not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/service-types": { "get": { "summary": "List service types", "description": "Returns tenant service types. Use this endpoint to resolve custom_service_type_id before creating or updating services and products in the service catalog.", "tags": [ "Service Types", "Service Catalog" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "service", "x-chat-callable": true, "x-chat-display-name": "List Service Types", "x-chat-rbac-resource": "service", "x-chat-approval-required": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "boolean" }, "required": false, "name": "is_active", "in": "query" }, { "schema": { "type": "integer", "minimum": 1 }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "integer", "minimum": 1, "maximum": 100 }, "required": false, "name": "limit", "in": "query" } ], "responses": { "200": { "description": "Service types returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedServiceTypeEnvelope" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/service-types/{id}": { "get": { "summary": "Get service type", "description": "Returns a single tenant service type by UUID.", "tags": [ "Service Types", "Service Catalog" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "service", "x-chat-callable": true, "x-chat-display-name": "Get Service Type", "x-chat-rbac-resource": "service", "x-chat-approval-required": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Service type UUID." }, "required": true, "description": "Service type UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Service type returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ServiceTypeEnvelope" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Service type not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/webhooks": { "get": { "summary": "List webhooks", "description": "Lists tenant webhooks with pagination and filter query fields.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "read", "x-controller-method": "ApiWebhookController.list()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "url", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "event_type", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_active", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "is_test_mode", "in": "query" }, { "schema": { "type": "string", "enum": [ "json", "xml", "form_data", "custom" ] }, "required": false, "name": "payload_format", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "has_failures", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "last_delivery_from", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "last_delivery_to", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "delivery_rate_min", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "delivery_rate_max", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Paginated webhook list.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookListResponseV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } }, "post": { "summary": "Create webhook", "description": "Creates a webhook configuration. Supports per-entity payload_fields allowlists; an undefined or null map yields the full payload.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "create", "x-controller-method": "ApiWebhookController.create()" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateWebhookBodyV1" } } } }, "responses": { "201": { "description": "Webhook created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "409": { "description": "Webhook already exists.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/analytics": { "get": { "summary": "Get system webhook analytics", "description": "Returns aggregated webhook delivery analytics for the tenant over a date window.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "analytics", "x-controller-method": "ApiWebhookController.getAnalytics()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "webhook_id", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "date_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "date_to", "in": "query" } ], "responses": { "200": { "description": "Aggregated analytics returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookAnalyticsEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/events": { "get": { "summary": "List available webhook events", "description": "Returns the supported webhook event types from webhookEventTypeSchema (ticket.*, project.*, invoice.*, etc.).", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "read", "x-controller-method": "ApiWebhookController.listEvents()" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Event type list.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEventListEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/templates": { "get": { "summary": "List webhook templates", "description": "Returns webhook templates visible to the tenant (own + system templates).", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "read", "x-controller-method": "ApiWebhookController.listTemplates()" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Template list.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookTemplateListEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } }, "post": { "summary": "Create webhook template", "description": "Creates a reusable webhook template. Requires system_settings RBAC.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "system_settings", "x-controller-method": "ApiWebhookController.createTemplate()" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookTemplateBodyV1" } } } }, "responses": { "201": { "description": "Template created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookTemplateEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/templates/{id}": { "get": { "summary": "Get webhook template detail (webhook getById wiring)", "description": "Template detail route delegates to ApiWebhookController.getById() and looks up rows from the webhooks table by the URL id, not webhook_templates. Calling with a true template_id will return 404.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "read", "x-controller-method": "ApiWebhookController.getById()", "x-route-to-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "required": true, "description": "Webhook UUID from webhooks.webhook_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Webhook detail returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } }, "put": { "summary": "Update webhook template (webhook update wiring)", "description": "Template update route delegates to ApiWebhookController.update() and operates on webhooks rows, not webhook_templates.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "update", "x-controller-method": "ApiWebhookController.update()", "x-route-to-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "required": true, "description": "Webhook UUID from webhooks.webhook_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateWebhookBodyV1" } } } }, "responses": { "200": { "description": "Webhook updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } }, "delete": { "summary": "Delete webhook template (webhook delete wiring)", "description": "Template delete route delegates to ApiWebhookController.delete() and removes a webhooks row, not a webhook_templates row.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "delete", "x-controller-method": "ApiWebhookController.delete()", "x-route-to-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "required": true, "description": "Webhook UUID from webhooks.webhook_id.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Webhook deleted." }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/templates/{id}/create": { "post": { "summary": "Create webhook from template", "description": "Instantiates a webhook configuration from a template, merging caller-supplied overrides.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "create", "x-controller-method": "ApiWebhookController.useTemplate()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "required": true, "description": "Webhook UUID from webhooks.webhook_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookTemplateCreateBodyV1" } } } }, "responses": { "201": { "description": "Webhook created from template.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/test": { "post": { "summary": "Test webhook configuration", "description": "Sends a one-off test delivery. Pass webhook_id to reuse stored configuration, or override_url/test_payload for ad-hoc testing.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "test", "x-controller-method": "ApiWebhookController.test()" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookTestBodyV1" } } } }, "responses": { "200": { "description": "Test delivery executed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookTestResultEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/verify": { "post": { "summary": "Verify webhook signature", "description": "Verifies an X-Alga-Signature header value against a stored signing secret. Returns { valid: true } only for sha256 with a matching v1 signature.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "verify", "x-controller-method": "ApiWebhookController.verifySignature()" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookSignatureBodyV1" } } } }, "responses": { "200": { "description": "Signature verification result.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookSignatureValidationEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/{id}": { "get": { "summary": "Get webhook", "description": "Returns a single webhook by id.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "read", "x-controller-method": "ApiWebhookController.getById()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "required": true, "description": "Webhook UUID from webhooks.webhook_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Webhook detail.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } }, "put": { "summary": "Update webhook", "description": "Updates a webhook by id. Body accepts the same fields as create, all optional.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "update", "x-controller-method": "ApiWebhookController.update()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "required": true, "description": "Webhook UUID from webhooks.webhook_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateWebhookBodyV1" } } } }, "responses": { "200": { "description": "Webhook updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } }, "delete": { "summary": "Delete webhook", "description": "Deletes a webhook by id.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "delete", "x-controller-method": "ApiWebhookController.delete()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "required": true, "description": "Webhook UUID from webhooks.webhook_id.", "name": "id", "in": "path" } ], "responses": { "204": { "description": "Webhook deleted." }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/{id}/analytics": { "get": { "summary": "Get webhook analytics", "description": "Returns delivery analytics for a single webhook over a date window.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "analytics", "x-controller-method": "ApiWebhookController.getWebhookAnalytics()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "required": true, "description": "Webhook UUID from webhooks.webhook_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid" }, "required": false, "name": "webhook_id", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "date_from", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "date_to", "in": "query" } ], "responses": { "200": { "description": "Per-webhook analytics returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookAnalyticsEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/{id}/deliveries": { "get": { "summary": "List webhook deliveries", "description": "Returns paginated delivery history for a webhook id, optionally filtered by status and date window.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "read", "x-controller-method": "ApiWebhookController.getDeliveries()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "required": true, "description": "Webhook UUID from webhooks.webhook_id.", "name": "id", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string", "enum": [ "pending", "delivered", "failed", "retrying", "abandoned" ] }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "from_date", "in": "query" }, { "schema": { "type": "string", "format": "date-time" }, "required": false, "name": "to_date", "in": "query" } ], "responses": { "200": { "description": "Paginated delivery list.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookDeliveryListResponseV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/{id}/deliveries/{delivery_id}": { "get": { "summary": "Get delivery detail", "description": "Returns a single delivery record (request/response payloads, status, retry pointer).", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "read", "x-controller-method": "ApiWebhookController.getDelivery()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "required": true, "description": "Webhook UUID from webhooks.webhook_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Delivery UUID from webhook_deliveries.delivery_id (URL-derived)." }, "required": true, "description": "Delivery UUID from webhook_deliveries.delivery_id (URL-derived).", "name": "delivery_id", "in": "path" } ], "responses": { "200": { "description": "Delivery detail returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookDeliveryEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/{id}/deliveries/{delivery_id}/retry": { "post": { "summary": "Retry delivery", "description": "Re-sends a previously failed delivery and records a new attempt on the same delivery row.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "retry", "x-controller-method": "ApiWebhookController.retryDelivery()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "required": true, "description": "Webhook UUID from webhooks.webhook_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Delivery UUID from webhook_deliveries.delivery_id (URL-derived)." }, "required": true, "description": "Delivery UUID from webhook_deliveries.delivery_id (URL-derived).", "name": "delivery_id", "in": "path" } ], "responses": { "200": { "description": "Retry attempted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookDeliveryEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/{id}/health": { "get": { "summary": "Get webhook health", "description": "Derives status (healthy | failing | disabled), success_rate, and last delivery timestamps from the webhook stats counters.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "read", "x-controller-method": "ApiWebhookController.getHealth()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "required": true, "description": "Webhook UUID from webhooks.webhook_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Health status returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookHealthEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/{id}/secret/rotate": { "post": { "summary": "Rotate webhook secret", "description": "Generates a new 32-byte base64url signing secret, persists it via webhookModel.update, and returns the secret in the response. The secret is only available at rotation time.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "manage_security", "x-controller-method": "ApiWebhookController.rotateSecret()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "required": true, "description": "Webhook UUID from webhooks.webhook_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Secret rotated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookSecretRotationEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/{id}/subscriptions": { "get": { "summary": "List webhook event subscriptions", "description": "Returns the event types the webhook is subscribed to. Subscriptions are stored on the webhooks row (event_types column), not in a separate subscription table.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "read", "x-controller-method": "ApiWebhookController.getSubscriptions()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "required": true, "description": "Webhook UUID from webhooks.webhook_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Subscription list returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookSubscriptionsEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/webhooks/{id}/test": { "post": { "summary": "Test webhook by id", "description": "Sends a signed test delivery to the webhook URL using the stored signing secret and records the attempt in webhook_deliveries with is_test=true.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiWebhookController.authenticate()", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "webhook", "x-rbac-action": "test", "x-controller-method": "ApiWebhookController.testById()" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Webhook UUID from webhooks.webhook_id." }, "required": true, "description": "Webhook UUID from webhooks.webhook_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookTestBodyV1" } } } }, "responses": { "200": { "description": "Test delivery executed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookTestResultEnvelopeV1" } } } }, "400": { "description": "Invalid request payload, query, or webhook id format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or key user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "403": { "description": "Webhook RBAC permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "404": { "description": "Webhook, delivery, template, or signing secret not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } }, "500": { "description": "Unexpected webhook operation failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookApiErrorV1" } } } } } } }, "/api/v1/workflows": { "get": { "summary": "List workflows (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } }, "post": { "summary": "Create workflow (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/analytics": { "get": { "summary": "Get workflow analytics (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/events": { "get": { "summary": "List workflow events (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } }, "post": { "summary": "Create workflow event (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/events/{id}": { "get": { "summary": "Get workflow event by id (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/executions": { "get": { "summary": "List workflow executions (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } }, "post": { "summary": "Create workflow execution (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/executions/bulk": { "post": { "summary": "Bulk create workflow executions (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/executions/bulk-action": { "post": { "summary": "Bulk action workflow executions (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/executions/{id}": { "get": { "summary": "Get workflow execution by id (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } }, "put": { "summary": "Update workflow execution (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/executions/{id}/cancel": { "post": { "summary": "Cancel workflow execution (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/executions/{id}/pause": { "post": { "summary": "Pause workflow execution (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/executions/{id}/restart": { "post": { "summary": "Restart workflow execution (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/executions/{id}/resume": { "post": { "summary": "Resume workflow execution (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/export": { "get": { "summary": "Export workflows (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/import": { "post": { "summary": "Import workflows (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/search": { "get": { "summary": "Search workflows (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/tasks": { "get": { "summary": "List workflow tasks (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } }, "post": { "summary": "Create workflow task (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/tasks/bulk-assign": { "post": { "summary": "Bulk assign workflow tasks (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/tasks/{id}": { "get": { "summary": "Get workflow task by id (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } }, "put": { "summary": "Update workflow task (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/tasks/{id}/claim": { "post": { "summary": "Claim workflow task (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/tasks/{id}/complete": { "post": { "summary": "Complete workflow task (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/templates": { "get": { "summary": "List workflow templates (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } }, "post": { "summary": "Create workflow template (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/templates/{id}": { "delete": { "summary": "Delete workflow template (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } }, "get": { "summary": "Get workflow template by id (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } }, "put": { "summary": "Update workflow template (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/workflows/{id}": { "delete": { "summary": "Delete workflow by id (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } }, "get": { "summary": "Get workflow by id (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } }, "put": { "summary": "Update workflow (route inventory only)", "description": "This operation is currently present only in generated route inventory. No corresponding Next.js handler exists under server/src/app/api/v1/workflows in this worktree. Runtime behavior is middleware-dependent: missing/invalid x-api-key can return 401 before routing; with middleware requirements satisfied, Next.js returns not-found for the absent handler. Existing workflow APIs in this codebase are implemented under /api/workflow-definitions, /api/workflow-runs, and /api/workflow/events rather than /api/v1/workflows paths.", "tags": [ "Workflows v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-route-inventory-only": true, "x-route-file-missing": "server/src/app/api/v1/workflows/**/route.ts", "x-family-status": "missing-handler" }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "x-api-key missing/invalid at middleware.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingApiError" } } } }, "404": { "description": "No route handler exists for this inventory-only /api/v1/workflows path.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/WorkflowV1MissingRouteResponse" } } } } } } }, "/api/v1/projects/{id}/task-status-mappings": { "get": { "summary": "List project task status mappings", "description": "Returns the task status mappings configured for the specified project. Use this endpoint to translate a human-readable status label such as \"In Progress\" into a project_status_mapping_id UUID before updating a task.", "tags": [ "Projects" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "project", "x-chat-callable": true, "x-chat-display-name": "List Project Task Status Mappings", "x-chat-rbac-resource": "project", "x-chat-approval-required": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Project UUID." }, "required": true, "description": "Project UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Project task status mappings returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProjectTaskStatusMappingListEnvelope" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Project not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/projects/{id}/tasks": { "get": { "summary": "List project tasks", "description": "Returns all tasks for the specified project UUID. Use this endpoint to identify a task by task_name before fetching or updating it.", "tags": [ "Projects" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "project", "x-chat-callable": true, "x-chat-display-name": "List Project Tasks", "x-chat-rbac-resource": "project", "x-chat-approval-required": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Project UUID." }, "required": true, "description": "Project UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Project tasks returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProjectTaskListEnvelope" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Project not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/projects/{id}/phases/{phaseId}/tasks": { "post": { "summary": "Create project phase task", "description": "Creates a new task in the specified project phase. Resolve phaseId from the project phases and project_status_mapping_id from the project task status mappings before calling this endpoint.", "tags": [ "Projects" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "project", "x-chat-callable": true, "x-chat-display-name": "Create Project Phase Task", "x-chat-rbac-resource": "project", "x-chat-approval-required": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Project UUID." }, "required": true, "description": "Project UUID.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Project phase UUID." }, "required": true, "description": "Project phase UUID.", "name": "phaseId", "in": "path" } ], "requestBody": { "description": "Project task creation payload.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProjectTaskCreateRequest" }, "description": "Project task creation payload." } } }, "responses": { "201": { "description": "Project task created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProjectTaskEnvelope" } } } }, "400": { "description": "Validation error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Project or phase not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "List project phase tasks", "description": "Returns all tasks for the specified project phase. Both id and phaseId must be UUID path parameters.", "tags": [ "Projects" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "project", "x-chat-callable": true, "x-chat-display-name": "List Project Phase Tasks", "x-chat-rbac-resource": "project", "x-chat-approval-required": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Project UUID." }, "required": true, "description": "Project UUID.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Project phase UUID." }, "required": true, "description": "Project phase UUID.", "name": "phaseId", "in": "path" } ], "responses": { "200": { "description": "Project phase tasks returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProjectTaskListEnvelope" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Project phase not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/projects/tasks/{taskId}": { "get": { "summary": "Get project task", "description": "Returns a single project task by its task UUID.", "tags": [ "Projects" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "project", "x-chat-callable": true, "x-chat-display-name": "Get Project Task", "x-chat-rbac-resource": "project", "x-chat-approval-required": false }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Project task UUID." }, "required": true, "description": "Project task UUID.", "name": "taskId", "in": "path" } ], "responses": { "200": { "description": "Project task returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProjectTaskEnvelope" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Project task not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "put": { "summary": "Update project task", "description": "Updates a project task by task UUID. Use project_status_mapping_id when changing task status, and only send fields defined in the request schema.", "tags": [ "Projects" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "project", "x-chat-callable": true, "x-chat-display-name": "Update Project Task", "x-chat-rbac-resource": "project", "x-chat-approval-required": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Project task UUID." }, "required": true, "description": "Project task UUID.", "name": "taskId", "in": "path" } ], "requestBody": { "description": "Project task update payload.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProjectTaskUpdateRequest" }, "description": "Project task update payload." } } }, "responses": { "200": { "description": "Project task updated successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProjectTaskEnvelope" } } } }, "400": { "description": "Validation error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks the required permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Project task not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "delete": { "summary": "Delete project task", "description": "Permanently deletes a project task by task UUID. The controller resolves the task's parent project through project_tasks.phase_id and project_phases.project_id, checks project:delete permission, and verifies row-level read access to that project before deleting the task. Returns 204 No Content on success.", "tags": [ "Projects" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "project", "x-chat-callable": true, "x-chat-display-name": "Delete Project Task", "x-chat-rbac-resource": "project", "x-chat-approval-required": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Project task UUID." }, "required": true, "description": "Project task UUID.", "name": "taskId", "in": "path" } ], "responses": { "204": { "description": "Project task deleted successfully." }, "401": { "description": "Authentication failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Authenticated user lacks project delete permission or row-level access to the parent project.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Project task not found, or its parent project could not be resolved.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/projects": { "get": { "summary": "List projects", "description": "Lists projects using ApiProjectController.list() with authorization-aware pagination.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Create project", "description": "Creates a project via ApiProjectController.create().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1CreateProjectBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/projects/bulk-assign": { "put": { "summary": "Bulk assign projects", "description": "Bulk assignment operation via ApiProjectController.bulkAssign().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/projects/bulk-status": { "put": { "summary": "Bulk update project status", "description": "Bulk status update operation via ApiProjectController.bulkStatusUpdate().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/projects/bulk-update": { "put": { "summary": "Bulk update projects", "description": "Bulk update operation via ApiProjectController.bulkUpdate().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/projects/export": { "get": { "summary": "Export projects", "description": "Exports projects via ApiProjectController.export().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/projects/search": { "get": { "summary": "Search projects", "description": "Searches projects via ApiProjectController.search().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/projects/stats": { "get": { "summary": "Get project stats", "description": "Returns project aggregate statistics for authorized projects.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/projects/tasks/{taskId}/checklist": { "get": { "summary": "List task checklist items", "description": "Reads checklist items for project task UUID through ApiProjectController.getTaskChecklist().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Project task UUID from project_tasks.task_id." }, "required": true, "description": "Project task UUID from project_tasks.task_id.", "name": "taskId", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Create task checklist item", "description": "Creates checklist item for project task UUID via ApiProjectController.createChecklistItem().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Project task UUID from project_tasks.task_id." }, "required": true, "description": "Project task UUID from project_tasks.task_id.", "name": "taskId", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/projects/{id}": { "delete": { "summary": "Delete project", "description": "Deletes project by project UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "get": { "summary": "Get project", "description": "Returns one project by project UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "put": { "summary": "Update project", "description": "Updates project by project UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/projects/{id}/phases": { "get": { "summary": "List project phases", "description": "Lists phases for project UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Create project phase", "description": "Creates a project phase under project UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/projects/{id}/phases/{phaseId}": { "delete": { "summary": "Delete project phase", "description": "Deletes phase UUID under project UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Project UUID from projects.project_id." }, "required": true, "description": "Project UUID from projects.project_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Project phase UUID from project_phases.phase_id." }, "required": true, "description": "Project phase UUID from project_phases.phase_id.", "name": "phaseId", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "put": { "summary": "Update project phase", "description": "Updates phase UUID under project UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Project UUID from projects.project_id." }, "required": true, "description": "Project UUID from projects.project_id.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Project phase UUID from project_phases.phase_id." }, "required": true, "description": "Project phase UUID from project_phases.phase_id.", "name": "phaseId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/projects/{id}/tickets": { "get": { "summary": "List project tickets", "description": "Lists tickets linked to project UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "project" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for project resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/schedules": { "get": { "summary": "List schedule entries", "description": "Lists schedule entries via ApiTimeSheetController.listScheduleEntries().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Create schedule entry", "description": "Creates schedule entry via ApiTimeSheetController.createScheduleEntry().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/schedules/search": { "get": { "summary": "Search schedules", "description": "Current route delegates to ApiTimeSheetController.list() (time sheet list path), not schedule-specific search.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/schedules/{id}": { "delete": { "summary": "Delete schedule entry", "description": "Deletes schedule entry via ApiTimeSheetController.deleteScheduleEntry().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "get": { "summary": "Get schedule entry", "description": "Current route delegates to ApiTimeSheetController.getById() (time sheet read path), not schedule-specific getter.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "put": { "summary": "Update schedule entry", "description": "Updates schedule entry via ApiTimeSheetController.updateScheduleEntry().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/schedules/{id}/conflicts": { "get": { "summary": "Get schedule conflicts", "description": "Current route delegates to ApiTimeSheetController.list() rather than conflict-specific schedule logic.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tags": { "get": { "summary": "List tags", "description": "Lists tags via ApiTagController.list().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Create tag", "description": "Creates a tag via ApiTagController.create().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1CreateTagBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tags/analytics": { "get": { "summary": "Get tag analytics", "description": "Returns tag analytics aggregation payload.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tags/bulk": { "delete": { "summary": "Bulk delete tags", "description": "Bulk delete tag operation.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Bulk create tags", "description": "Bulk create tag operation.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tags/bulk/merge": { "post": { "summary": "Bulk merge tags", "description": "Bulk merge tags into target tag.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tags/bulk/tag": { "post": { "summary": "Bulk tag entities", "description": "Bulk add tags to entities.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tags/bulk/untag": { "delete": { "summary": "Bulk untag entities", "description": "Bulk remove tags from entities.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tags/by-text": { "delete": { "summary": "Delete tags by text", "description": "Deletes tags matching text criteria.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tags/cloud": { "get": { "summary": "Get tag cloud", "description": "Returns weighted tag cloud data.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tags/entity/{entityType}/{entityId}": { "delete": { "summary": "Remove tags from entity", "description": "Removes tag set from entity route params/body.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "description": "Tagged entity type from route segment." }, "required": true, "description": "Tagged entity type from route segment.", "name": "entityType", "in": "path" }, { "schema": { "type": "string", "description": "Tagged entity id from route segment." }, "required": true, "description": "Tagged entity id from route segment.", "name": "entityId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "get": { "summary": "List tags for entity", "description": "Lists tags attached to entity route params.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "description": "Tagged entity type from route segment." }, "required": true, "description": "Tagged entity type from route segment.", "name": "entityType", "in": "path" }, { "schema": { "type": "string", "description": "Tagged entity id from route segment." }, "required": true, "description": "Tagged entity id from route segment.", "name": "entityId", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Attach tags to entity", "description": "Adds tags to entity route params/body.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "description": "Tagged entity type from route segment." }, "required": true, "description": "Tagged entity type from route segment.", "name": "entityType", "in": "path" }, { "schema": { "type": "string", "description": "Tagged entity id from route segment." }, "required": true, "description": "Tagged entity id from route segment.", "name": "entityId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tags/search": { "get": { "summary": "Search tags", "description": "Searches tags via ApiTagController.search().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tags/{id}": { "delete": { "summary": "Delete tag", "description": "Deletes tag UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "get": { "summary": "Get tag", "description": "Gets tag UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "put": { "summary": "Update tag", "description": "Updates tag UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tags/{id}/colors": { "put": { "summary": "Update tag colors", "description": "Updates tag color attributes.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tags/{id}/text": { "put": { "summary": "Update tag text", "description": "Updates tag display text.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "tag" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for tag resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets": { "get": { "summary": "List tickets", "description": "Lists tickets via ApiTicketController.list() with authorization-aware pagination.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Create ticket", "description": "Creates ticket via ApiTicketController.create().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1CreateTicketBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/from-asset": { "post": { "summary": "Create ticket from asset", "description": "Creates ticket from asset context via ApiTicketController.createFromAsset().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "get": { "summary": "Create ticket from asset (POST only)", "description": "This path creates a ticket from an asset via POST. GET is not supported and returns 405 Method Not Allowed.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "ticket", "x-rbac-action": "read", "x-method-not-allowed": true }, "x-alga-products": [ "psa" ], "responses": { "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "405": { "description": "Method not allowed; use POST to create a ticket from an asset.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/search": { "get": { "summary": "Search tickets", "description": "Searches tickets via ApiTicketController.search().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/stats": { "get": { "summary": "Get ticket stats", "description": "Returns ticket aggregate stats for authorized tickets.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/{id}": { "delete": { "summary": "Delete ticket", "description": "Deletes ticket UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "409": { "description": "Deletion blocked: the resource has dependent records that must be removed or reassigned first. The error details list the blocking dependencies.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "get": { "summary": "Get ticket", "description": "Gets ticket UUID with authorization check.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "put": { "summary": "Update ticket", "description": "Updates ticket UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/{id}/assignment": { "put": { "summary": "Update ticket assignment", "description": "Updates ticket assignment target.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/{id}/comments": { "get": { "summary": "List ticket comments", "description": "Lists comments for ticket UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Add ticket comment", "description": "Adds comment to ticket UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/{id}/status": { "put": { "summary": "Update ticket status", "description": "Updates status for ticket UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/{id}/time-entries": { "get": { "summary": "List ticket time entries", "description": "Returns the caller's time entries on the ticket plus, when permitted, other team members' entries (or an anonymized aggregate when the caller lacks timesheet:read_all).", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/{id}/bundle": { "get": { "summary": "Get ticket bundle", "description": "Returns bundle membership for the ticket: role (master, child, or standalone), the master ticket, child tickets, and bundle settings.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Create ticket bundle", "description": "Bundles the given child tickets under ticket {id} as the master, with a sync mode of link_only or sync_updates.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "delete": { "summary": "Unbundle ticket", "description": "Unbundles master {id}, detaching all child tickets and removing bundle settings.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/{id}/bundle/children": { "post": { "summary": "Add bundle children", "description": "Adds child tickets to the existing bundle mastered by {id}.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/{id}/bundle/children/{childId}": { "delete": { "summary": "Remove bundle child", "description": "Removes child {childId} from its bundle; removes bundle settings when no children remain.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/{id}/bundle/promote": { "post": { "summary": "Promote bundle master", "description": "Promotes a child ticket to be the new bundle master, re-pointing the remaining children.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/{id}/bundle/settings": { "put": { "summary": "Update ticket bundle settings", "description": "Updates the bundle mode and/or reopen-on-child-reply policy for master {id}.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "ticket" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-entries": { "get": { "summary": "List time entries", "description": "Lists time entries via ApiBaseController list flow.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Create time entry", "description": "Creates time entry.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1CreateTimeEntryBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-entries/active-session": { "get": { "summary": "Get active tracking session", "description": "Returns active tracking session for current user context.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-entries/approve": { "post": { "summary": "Approve time entries", "description": "Approves time entries payload.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-entries/bulk": { "delete": { "summary": "Bulk delete time entries", "description": "Bulk delete time entry operation.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Bulk create time entries", "description": "Bulk create time entry operation.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "put": { "summary": "Bulk update time entries", "description": "Bulk update time entry operation.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-entries/export": { "get": { "summary": "Export time entries", "description": "Exports time entries with optional filter/format query.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-entries/request-changes": { "post": { "summary": "Request time entry changes", "description": "Requests changes for submitted time entries.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-entries/search": { "get": { "summary": "Search time entries", "description": "Searches time entries with controller-specific filters.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-entries/start-tracking": { "post": { "summary": "Start time tracking", "description": "Starts active tracking session.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-entries/stats": { "get": { "summary": "Get time entry stats", "description": "Returns time entry statistics.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-entries/stop-tracking/{sessionId}": { "post": { "summary": "Stop time tracking", "description": "Stops active tracking session by session id.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Active time-tracking session identifier." }, "required": true, "description": "Active time-tracking session identifier.", "name": "sessionId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-entries/templates": { "get": { "summary": "List time entry templates", "description": "Lists available time entry templates.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-entries/{id}": { "delete": { "summary": "Delete time entry", "description": "Deletes time entry UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "get": { "summary": "Get time entry", "description": "Gets time entry UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "put": { "summary": "Update time entry", "description": "Updates time entry UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_entry" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_entry resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-periods": { "get": { "summary": "List time periods", "description": "Lists time periods via ApiTimeSheetController.listTimePeriods().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Create time period", "description": "Creates time period via ApiTimeSheetController.createTimePeriod().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-periods/current": { "get": { "summary": "Get current time period", "description": "Current route delegates to ApiTimeSheetController.list() and does not call a dedicated current-period method.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-periods/{id}": { "delete": { "summary": "Delete time period", "description": "Deletes time period UUID via deleteTimePeriod().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "get": { "summary": "Get time period", "description": "Gets time period UUID via getTimePeriod().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "put": { "summary": "Update time period", "description": "Updates time period UUID via updateTimePeriod().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-periods/{id}/close": { "post": { "summary": "Close time period", "description": "Current route delegates to ApiTimeSheetController.update() (time sheet update flow), not dedicated time-period close logic.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-periods/{id}/reopen": { "post": { "summary": "Reopen time period", "description": "Current route delegates to ApiTimeSheetController.update() (time sheet update flow), not dedicated time-period reopen logic.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-sheets": { "get": { "summary": "List time sheets", "description": "Lists time sheets via ApiTimeSheetController.list().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Create time sheet", "description": "Creates time sheet via create() path.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1CreateTimeSheetBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-sheets/bulk": { "post": { "summary": "Bulk time sheet operation", "description": "Current route delegates to ApiTimeSheetController.list() (read path) instead of bulk approval/update logic.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-sheets/export": { "get": { "summary": "Export time sheets", "description": "Exports time sheets using ApiTimeSheetController.export().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-sheets/search": { "get": { "summary": "Search time sheets", "description": "Current route delegates to ApiTimeSheetController.list() rather than search().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-sheets/{id}": { "delete": { "summary": "Delete time sheet", "description": "Deletes time sheet UUID via ApiBase delete path.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "get": { "summary": "Get time sheet", "description": "Gets time sheet UUID with details via getWithDetails().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "put": { "summary": "Update time sheet", "description": "Updates time sheet UUID.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-sheets/{id}/add-entry": { "post": { "summary": "Add time sheet entry", "description": "Current route delegates to ApiTimeSheetController.create() (time sheet create path), not entry-add-specific logic.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-sheets/{id}/approve": { "post": { "summary": "Approve time sheet", "description": "Approves time sheet UUID via approve().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-sheets/{id}/entries": { "get": { "summary": "List time sheet entries", "description": "Current route delegates to ApiTimeSheetController.list() rather than a per-sheet entries reader.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-sheets/{id}/reject": { "post": { "summary": "Reject time sheet", "description": "Current route delegates to ApiTimeSheetController.update() instead of reject-specific flow.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-sheets/{id}/remove-entry": { "delete": { "summary": "Remove time sheet entry", "description": "Current route delegates to ApiTimeSheetController.delete() instead of entry-remove-specific flow.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "204": { "description": "Delete-like operation can return no content when implemented that way." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-sheets/{id}/request-changes": { "post": { "summary": "Request time sheet changes", "description": "Requests changes via requestChanges().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-sheets/{id}/reverse-approval": { "post": { "summary": "Reverse time sheet approval", "description": "Reverses approval via reverseApproval().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-sheets/{id}/submit": { "post": { "summary": "Submit time sheet", "description": "Submits time sheet for approval via submit().", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1GenericBody" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/time-sheets/{id}/summary": { "get": { "summary": "Get time sheet summary", "description": "Current route delegates to ApiTimeSheetController.list() instead of summary-specific computation.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or controller-specific equivalent", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "time_sheet", "x-route-controller-mismatch": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "fields", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "query", "in": "query" } ], "responses": { "200": { "description": "Collection response returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiPaginated" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for time_sheet resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Target resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/priorities": { "get": { "summary": "List ticket priorities", "description": "Returns the tenant ticket priorities (item_type=ticket), ordered by name.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "ticket", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "responses": { "200": { "description": "Ticket priorities.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/statuses": { "get": { "summary": "List ticket statuses", "description": "Returns board-owned ticket statuses for the tenant, optionally filtered by board_id, sorted by board then order_number.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "ticket", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Filter statuses to this board." }, "required": false, "description": "Filter statuses to this board.", "name": "board_id", "in": "query" } ], "responses": { "200": { "description": "Ticket statuses.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/{id}/comments/{commentId}": { "put": { "summary": "Update a ticket comment", "description": "Updates the text of a comment on a ticket. Comment text must be 1–5000 visible characters.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "ticket", "x-rbac-action": "update" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Ticket UUID." }, "required": true, "description": "Ticket UUID.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Comment UUID." }, "required": true, "description": "Comment UUID.", "name": "commentId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1TicketCommentUpdateBody" } } } }, "responses": { "200": { "description": "Updated comment.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Ticket or sub-resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/{id}/comments/{commentId}/reactions": { "post": { "summary": "Toggle a comment reaction", "description": "Adds or removes the given emoji reaction on a comment for the authenticated user. Returns { added: boolean } indicating whether the reaction was added (true) or removed (false).", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "ticket", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Ticket UUID." }, "required": true, "description": "Ticket UUID.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Comment UUID." }, "required": true, "description": "Comment UUID.", "name": "commentId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1TicketReactionBody" } } } }, "responses": { "200": { "description": "Reaction toggled.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Ticket or sub-resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/{id}/documents": { "get": { "summary": "List ticket documents", "description": "Returns the documents (file attachments) associated with a ticket.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "ticket", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Ticket documents.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Ticket or sub-resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Upload a ticket document", "description": "Uploads a file as a document attachment to a ticket. Send multipart/form-data with a `file` field. Returns the created document.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "ticket", "x-rbac-action": "update", "x-request-content-type": "multipart/form-data" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "201": { "description": "Document uploaded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Ticket or sub-resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/{id}/documents/{documentId}": { "get": { "summary": "Download a ticket document", "description": "Downloads a specific document attached to a ticket as binary data with Content-Type and Content-Disposition headers.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "ticket", "x-rbac-action": "read" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Ticket UUID." }, "required": true, "description": "Ticket UUID.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Document UUID." }, "required": true, "description": "Document UUID.", "name": "documentId", "in": "path" } ], "responses": { "200": { "description": "Binary document stream (Content-Disposition attachment).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Document not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "delete": { "summary": "Delete a ticket document", "description": "Removes a document from a ticket.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "ticket", "x-rbac-action": "update" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Ticket UUID." }, "required": true, "description": "Ticket UUID.", "name": "id", "in": "path" }, { "schema": { "type": "string", "format": "uuid", "description": "Document UUID." }, "required": true, "description": "Document UUID.", "name": "documentId", "in": "path" } ], "responses": { "200": { "description": "Document removed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Ticket or sub-resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/tickets/{id}/materials": { "get": { "summary": "List ticket materials", "description": "Returns the materials (service items/supplies) recorded against a ticket.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "ticket", "x-rbac-action": "read" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Ticket materials.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Ticket or sub-resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } }, "post": { "summary": "Add a ticket material", "description": "Associates a service item (material/supply) with a ticket, specifying quantity, rate, and currency. Returns the created material.", "tags": [ "Work Management v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "ticket", "x-rbac-action": "update" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "UUID path identifier from underlying resource tables." }, "required": true, "description": "UUID path identifier from underlying resource tables.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1TicketMaterialBody" } } } }, "responses": { "201": { "description": "Material added.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiSuccess" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "401": { "description": "API key missing/invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "403": { "description": "RBAC denied for ticket resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "404": { "description": "Ticket or sub-resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkV1ApiError" } } } } } } }, "/api/v1/quotes": { "get": { "summary": "List quotes", "description": "Lists quotes via ApiQuoteController.list() with authorization-aware filtering.", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_items", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_client", "in": "query" } ], "responses": { "200": { "description": "Paginated quotes returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiPaginatedV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } }, "post": { "summary": "Create quote", "description": "Creates quote via ApiQuoteController.create().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_items", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_client", "in": "query" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateQuoteBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/quotes/{id}": { "get": { "summary": "Get quote", "description": "Gets quote UUID via ApiQuoteController.getById().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } }, "put": { "summary": "Update quote", "description": "Updates quote UUID via ApiQuoteController.update().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsGenericBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } }, "delete": { "summary": "Delete quote", "description": "Deletes quote UUID via ApiQuoteController.delete().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "204": { "description": "Delete-like operation can return no content." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/quotes/{id}/activities": { "get": { "summary": "List quote activities", "description": "Lists quote activity history via listActivities().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/quotes/{id}/approve": { "post": { "summary": "Approve quote", "description": "Approves quote pending-approval state via approve(); includes self-approval guard.", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsGenericBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/quotes/{id}/convert": { "post": { "summary": "Convert quote", "description": "Converts quote to downstream entities via convert().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsGenericBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/quotes/{id}/convert/preview": { "get": { "summary": "Preview quote conversion", "description": "Returns conversion preview via conversionPreview().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/quotes/{id}/items": { "get": { "summary": "List quote items", "description": "Lists quote items via listItems().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } }, "post": { "summary": "Add quote item", "description": "Adds quote item via addItem().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsGenericBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/quotes/{id}/items/{itemId}": { "put": { "summary": "Update quote item", "description": "Updates quote item via updateItem().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsGenericBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } }, "delete": { "summary": "Delete quote item", "description": "Deletes quote item via deleteItem().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "204": { "description": "Delete-like operation can return no content." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/quotes/{id}/items/reorder": { "post": { "summary": "Reorder quote items", "description": "Reorders quote item sequence via reorderItems().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsGenericBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/quotes/{id}/remind": { "post": { "summary": "Send quote reminder", "description": "Sends reminder for quote via remind().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsGenericBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/quotes/{id}/request-changes": { "post": { "summary": "Request quote changes", "description": "Requests changes on pending-approval quote via requestChanges().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsGenericBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/quotes/{id}/resend": { "post": { "summary": "Resend quote", "description": "Resends quote via resend().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsGenericBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/quotes/{id}/revisions": { "get": { "summary": "List quote revisions", "description": "Lists quote versions via listVersions().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } }, "post": { "summary": "Create quote revision", "description": "Creates quote revision via createRevision().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsGenericBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/quotes/{id}/send": { "post": { "summary": "Send quote", "description": "Sends quote to recipient workflow via send().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsGenericBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/quotes/{id}/submit-for-approval": { "post": { "summary": "Submit quote for approval", "description": "Moves quote to pending-approval state via submitForApproval().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "quote", "x-read-authorization-resource": "billing" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Quote UUID from quotes.quote_id." }, "required": true, "description": "Quote UUID from quotes.quote_id.", "name": "id", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsGenericBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for quote resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/contracts": { "get": { "summary": "List contracts", "description": "Lists contracts via ApiContractLineController.listContracts() (v2 controller mounted under v1 route).", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "contract_line", "x-controller-origin": "ApiContractLineController (v2) mounted under v1 routes", "x-request-context-wiring-gap": "Controller requires req.context via requireRequestContext(req); v1 route lacks explicit withApiKeyAuth wrapper." }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_items", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_client", "in": "query" } ], "responses": { "200": { "description": "Paginated contracts returned.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiPaginatedV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for contract resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } }, "post": { "summary": "Create contract", "description": "Creates contract via ApiContractLineController.createContract() (v2 controller mounted under v1 route).", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "contract_line", "x-controller-origin": "ApiContractLineController (v2) mounted under v1 routes", "x-request-context-wiring-gap": "Controller requires req.context via requireRequestContext(req); v1 route lacks explicit withApiKeyAuth wrapper." }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "page", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "sort", "in": "query" }, { "schema": { "type": "string", "enum": [ "asc", "desc" ] }, "required": false, "name": "order", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "search", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "status", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_items", "in": "query" }, { "schema": { "type": "string", "enum": [ "true", "false" ] }, "required": false, "name": "include_client", "in": "query" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateContractBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for contract resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/contracts/{contractId}/contract-lines": { "post": { "summary": "Attach contract line", "description": "Attaches contract line to contract via addContractLine() using body contract_line_id/custom_rate.", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "contract_line", "x-controller-origin": "ApiContractLineController (v2) mounted under v1 routes", "x-request-context-wiring-gap": "Controller requires req.context via requireRequestContext(req); v1 route lacks explicit withApiKeyAuth wrapper." }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract UUID from contracts.contract_id." }, "required": true, "description": "Contract UUID from contracts.contract_id.", "name": "contractId", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "contract_line_id": { "type": "string", "format": "uuid" }, "custom_rate": { "type": "number" } }, "required": [ "contract_line_id" ] } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "201": { "description": "Create-like operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for contract resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/contracts/{contractId}/contract-lines/{contractLineId}": { "delete": { "summary": "Detach contract line", "description": "Detaches contract line from contract via removeContractLine().", "tags": [ "Quotes & Contracts v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "x-api-key validated in ApiBaseController.authenticate() or route middleware before handler", "x-tenant-header": "x-tenant-id (optional; inferred from API key when omitted)", "x-rbac-resource": "contract_line", "x-controller-origin": "ApiContractLineController (v2) mounted under v1 routes", "x-request-context-wiring-gap": "Controller requires req.context via requireRequestContext(req); v1 route lacks explicit withApiKeyAuth wrapper." }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "format": "uuid", "description": "Contract UUID from contracts.contract_id." }, "required": true, "description": "Contract UUID from contracts.contract_id.", "name": "contractId", "in": "path" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiSuccessV1" } } } }, "204": { "description": "Delete-like operation can return no content." }, "400": { "description": "Validation or request parsing failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "401": { "description": "API key missing/invalid or associated user missing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "403": { "description": "RBAC denied for contract resource action.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "404": { "description": "Target record not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } }, "500": { "description": "Unexpected controller/service failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuotesContractsApiErrorV1" } } } } } } }, "/api/v1/feature-access": { "post": { "summary": "Check feature access", "description": "Checks feature access via ApiPermissionController.checkFeatureAccess() using API key auth and permission read checks.", "tags": [ "Meta & Utility v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "permission", "x-auth-mechanism": "x-api-key validated by ApiPermissionController.authenticate()", "x-tenant-header": "x-tenant-id (optional)" }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FeatureAccessBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiSuccessV1" } } } }, "400": { "description": "Invalid request payload/query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "401": { "description": "Unauthorized (API key/session auth failure depending on route).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "403": { "description": "Permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "500": { "description": "Unexpected route/controller failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } } } } }, "/api/v1/feature-flags": { "get": { "summary": "Get feature flags", "description": "Returns feature flags for current session user; route uses getSession() and featureFlags service.", "tags": [ "Meta & Utility v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "Session user required in handler; global /api middleware may still require x-api-key presence", "x-tenant-header": "x-tenant-id (optional)", "x-session-auth-required": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "format", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "flags", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "period", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "language", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "package_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "version", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiSuccessV1" } } } }, "400": { "description": "Invalid request payload/query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "401": { "description": "Unauthorized (API key/session auth failure depending on route).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "403": { "description": "Permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "500": { "description": "Unexpected route/controller failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } } } }, "post": { "summary": "Evaluate feature flags with custom context", "description": "Evaluates requested/all flags with optional custom context payload; requires session user.", "tags": [ "Meta & Utility v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "Session user required in handler; global /api middleware may still require x-api-key presence", "x-tenant-header": "x-tenant-id (optional)", "x-session-auth-required": true }, "x-alga-products": [ "psa" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FeatureFlagsPostBodyV1" } } } }, "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiSuccessV1" } } } }, "400": { "description": "Invalid request payload/query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "401": { "description": "Unauthorized (API key/session auth failure depending on route).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "403": { "description": "Permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "500": { "description": "Unexpected route/controller failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } } } } }, "/api/v1/meta/docs": { "get": { "summary": "Get API docs", "description": "Returns Swagger UI HTML or redirects to OpenAPI endpoint depending on format query.", "tags": [ "Meta & Utility v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "metadata", "x-auth-mechanism": "ApiMetadataController.authenticate() + metadata read permission", "x-tenant-header": "x-tenant-id (optional)" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "format", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "flags", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "period", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "language", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "package_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "version", "in": "query" } ], "responses": { "200": { "description": "Swagger UI HTML or JSON redirect payload.", "content": { "text/html": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiSuccessV1" } } } }, "302": { "description": "Redirect to /api/v1/meta/openapi for non-html format." }, "400": { "description": "Invalid request payload/query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "401": { "description": "Unauthorized (API key/session auth failure depending on route).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "403": { "description": "Permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "500": { "description": "Unexpected route/controller failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } } } } }, "/api/v1/meta/endpoints": { "get": { "summary": "List API endpoints metadata", "description": "Returns endpoint inventory metadata via ApiMetadataController.getEndpoints().", "tags": [ "Meta & Utility v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "metadata", "x-auth-mechanism": "ApiMetadataController.authenticate() + metadata read permission", "x-tenant-header": "x-tenant-id (optional)" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "format", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "flags", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "period", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "language", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "package_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "version", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiSuccessV1" } } } }, "400": { "description": "Invalid request payload/query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "401": { "description": "Unauthorized (API key/session auth failure depending on route).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "403": { "description": "Permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "500": { "description": "Unexpected route/controller failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } } } } }, "/api/v1/meta/health": { "get": { "summary": "Get API health metadata", "description": "Returns API health metadata via ApiMetadataController.getHealth().", "tags": [ "Meta & Utility v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "metadata", "x-auth-mechanism": "ApiMetadataController.authenticate() + metadata read permission", "x-tenant-header": "x-tenant-id (optional)" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "format", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "flags", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "period", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "language", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "package_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "version", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiSuccessV1" } } } }, "400": { "description": "Invalid request payload/query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "401": { "description": "Unauthorized (API key/session auth failure depending on route).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "403": { "description": "Permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "500": { "description": "Unexpected route/controller failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } } } } }, "/api/v1/meta/openapi": { "get": { "summary": "Get generated OpenAPI metadata", "description": "Returns generated OpenAPI document JSON/YAML based on query format.", "tags": [ "Meta & Utility v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "metadata", "x-auth-mechanism": "ApiMetadataController.authenticate() + metadata read permission", "x-tenant-header": "x-tenant-id (optional)" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "format", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "flags", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "period", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "language", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "package_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "version", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiSuccessV1" } } } }, "400": { "description": "Invalid request payload/query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "401": { "description": "Unauthorized (API key/session auth failure depending on route).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "403": { "description": "Permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "500": { "description": "Unexpected route/controller failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } } } } }, "/api/v1/meta/permissions": { "get": { "summary": "List API permissions metadata", "description": "Returns permission metadata via ApiMetadataController.getPermissions().", "tags": [ "Meta & Utility v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "metadata", "x-auth-mechanism": "ApiMetadataController.authenticate() + metadata read permission", "x-tenant-header": "x-tenant-id (optional)" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "format", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "flags", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "period", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "language", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "package_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "version", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiSuccessV1" } } } }, "400": { "description": "Invalid request payload/query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "401": { "description": "Unauthorized (API key/session auth failure depending on route).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "403": { "description": "Permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "500": { "description": "Unexpected route/controller failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } } } } }, "/api/v1/meta/schemas": { "get": { "summary": "List API schemas metadata", "description": "Returns schema metadata via ApiMetadataController.getSchemas().", "tags": [ "Meta & Utility v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "metadata", "x-auth-mechanism": "ApiMetadataController.authenticate() + metadata read permission", "x-tenant-header": "x-tenant-id (optional)" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "format", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "flags", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "period", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "language", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "package_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "version", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiSuccessV1" } } } }, "400": { "description": "Invalid request payload/query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "401": { "description": "Unauthorized (API key/session auth failure depending on route).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "403": { "description": "Permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "500": { "description": "Unexpected route/controller failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } } } } }, "/api/v1/meta/sdk": { "get": { "summary": "Generate SDK metadata payload", "description": "Generates SDK structure payload for selected language/format via generateSdk().", "tags": [ "Meta & Utility v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "metadata", "x-auth-mechanism": "ApiMetadataController.authenticate() + metadata read permission", "x-tenant-header": "x-tenant-id (optional)" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "format", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "flags", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "period", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "language", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "package_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "version", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiSuccessV1" } } } }, "400": { "description": "Invalid request payload/query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "401": { "description": "Unauthorized (API key/session auth failure depending on route).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "403": { "description": "Permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "500": { "description": "Unexpected route/controller failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } } } } }, "/api/v1/meta/stats": { "get": { "summary": "Get API stats metadata", "description": "Returns API usage stats for requested period via getStats().", "tags": [ "Meta & Utility v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "metadata", "x-auth-mechanism": "ApiMetadataController.authenticate() + metadata read permission", "x-tenant-header": "x-tenant-id (optional)" }, "x-alga-products": [ "psa", "algadesk" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "format", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "flags", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "period", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "language", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "package_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "version", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiSuccessV1" } } } }, "400": { "description": "Invalid request payload/query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "401": { "description": "Unauthorized (API key/session auth failure depending on route).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "403": { "description": "Permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "500": { "description": "Unexpected route/controller failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } } } } }, "/api/v1/test-auth": { "get": { "summary": "Test API key auth", "description": "Debug endpoint wrapping withApiKeyAuth middleware; returns only non-sensitive authenticated context identifiers.", "tags": [ "Meta & Utility v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-rbac-resource": "auth", "x-auth-mechanism": "withApiKeyAuth from apiAuthMiddleware", "x-tenant-header": "x-tenant-id (optional)" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "format", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "flags", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "period", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "language", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "package_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "version", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiSuccessV1" } } } }, "400": { "description": "Invalid request payload/query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "401": { "description": "Unauthorized (API key/session auth failure depending on route).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "403": { "description": "Permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "500": { "description": "Unexpected route/controller failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } } } } }, "/api/v1/user/telemetry-decision": { "get": { "summary": "Get telemetry decision", "description": "Returns telemetry enabled/disabled decision from environment for current session user.", "tags": [ "Meta & Utility v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "Session user required in handler; global /api middleware may still require x-api-key presence", "x-tenant-header": "x-tenant-id (optional)", "x-session-auth-required": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "format", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "flags", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "period", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "language", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "package_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "version", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiSuccessV1" } } } }, "400": { "description": "Invalid request payload/query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "401": { "description": "Unauthorized (API key/session auth failure depending on route).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "403": { "description": "Permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "500": { "description": "Unexpected route/controller failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } } } } }, "/api/v1/user/telemetry-preferences": { "get": { "summary": "Get telemetry preferences", "description": "Read-only. Returns the usage-stats telemetry state, which is controlled by the ALGA_USAGE_STATS environment variable (not per-user); requires session user. There are no write verbs because the preference cannot be changed through the API.", "tags": [ "Meta & Utility v1" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-scoped": true, "x-auth-mechanism": "Session user required in handler; global /api middleware may still require x-api-key presence", "x-tenant-header": "x-tenant-id (optional)", "x-session-auth-required": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string" }, "required": false, "name": "format", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "flags", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "period", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "language", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "package_name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "version", "in": "query" } ], "responses": { "200": { "description": "Operation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiSuccessV1" } } } }, "400": { "description": "Invalid request payload/query.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "401": { "description": "Unauthorized (API key/session auth failure depending on route).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "403": { "description": "Permission denied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } }, "500": { "description": "Unexpected route/controller failure.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetaUtilityApiErrorV1" } } } } } } }, "/api/extensions/softwareone/agreements": { "get": { "summary": "List SoftwareOne agreements", "description": "Returns SoftwareOne agreement records available to the SoftwareOne extension. The current handler is an MVP placeholder backed by hardcoded dummy data; comments in the route indicate that a full implementation will validate permissions, derive tenant context, fetch from the SoftwareOne API, and apply filtering, sorting, and pagination. This route is not in the middleware API-key skip list, so callers must provide x-api-key at the API layer.", "tags": [ "SoftwareOne Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-placeholder-implementation": true, "x-extension": "softwareone" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Agreement list returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneAgreementsListResponse" } } } }, "401": { "description": "API key missing at middleware before the handler executes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MiddlewareUnauthorizedResponse" } } } }, "500": { "description": "Unexpected failure while fetching agreements.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } } } }, "post": { "summary": "Sync SoftwareOne agreements", "description": "Triggers the SoftwareOne agreements sync placeholder. The current handler parses the JSON body but ignores all fields, then returns a dummy count. A full implementation is expected to validate permissions, derive tenant context, call the SoftwareOne API, persist agreements, and return actual sync counts. This route is not in the middleware API-key skip list, so callers must provide x-api-key at the API layer.", "tags": [ "SoftwareOne Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-placeholder-implementation": true, "x-extension": "softwareone" }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Optional sync options. Currently ignored by the MVP handler.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneSyncRequest" }, "description": "Optional sync options. Currently ignored by the MVP handler." } } }, "responses": { "200": { "description": "Agreements sync placeholder completed successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneSyncResponse" } } } }, "401": { "description": "API key missing at middleware before the handler executes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MiddlewareUnauthorizedResponse" } } } }, "500": { "description": "Unexpected failure while syncing agreements.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } } } } }, "/api/extensions/softwareone/agreements/{id}": { "get": { "summary": "Get SoftwareOne agreement", "description": "Returns one SoftwareOne agreement by external agreement ID, including additional detail fields such as dates, payment terms, contact, and license count. The current handler is an MVP placeholder backed by hardcoded dummy data. The id path parameter is a SoftwareOne-style string such as agr-001, not an Alga UUID.", "tags": [ "SoftwareOne Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-placeholder-implementation": true, "x-extension": "softwareone" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "SoftwareOne external identifier, such as agr-001 or stmt-001. This is not an Alga UUID." }, "required": true, "description": "SoftwareOne external identifier, such as agr-001 or stmt-001. This is not an Alga UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Agreement detail returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneAgreementDetailResponse" } } } }, "401": { "description": "API key missing at middleware before the handler executes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MiddlewareUnauthorizedResponse" } } } }, "404": { "description": "Agreement not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } }, "500": { "description": "Unexpected failure while fetching agreement detail.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } } } } }, "/api/extensions/softwareone/statements": { "get": { "summary": "List SoftwareOne statements", "description": "Returns SoftwareOne billing statement records available to the SoftwareOne extension. The current handler is an MVP placeholder backed by hardcoded dummy data; comments in the route indicate that a full implementation will validate permissions, derive tenant context, fetch from the SoftwareOne API, and apply filtering, sorting, and pagination. This route is not in the middleware API-key skip list, so callers must provide x-api-key at the API layer.", "tags": [ "SoftwareOne Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-placeholder-implementation": true, "x-extension": "softwareone" }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Statement list returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneStatementsListResponse" } } } }, "401": { "description": "API key missing at middleware before the handler executes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MiddlewareUnauthorizedResponse" } } } }, "500": { "description": "Unexpected failure while fetching statements.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } } } }, "post": { "summary": "Sync SoftwareOne statements", "description": "Triggers the SoftwareOne statements sync placeholder. The current handler parses the JSON body but ignores all fields, then returns a dummy count. A full implementation is expected to validate permissions, derive tenant context, call the SoftwareOne API, persist statements, and return actual sync counts. This route is not in the middleware API-key skip list, so callers must provide x-api-key at the API layer.", "tags": [ "SoftwareOne Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-placeholder-implementation": true, "x-extension": "softwareone" }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Optional sync options. Currently ignored by the MVP handler.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneSyncRequest" }, "description": "Optional sync options. Currently ignored by the MVP handler." } } }, "responses": { "200": { "description": "Statements sync placeholder completed successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneSyncResponse" } } } }, "401": { "description": "API key missing at middleware before the handler executes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MiddlewareUnauthorizedResponse" } } } }, "500": { "description": "Unexpected failure while syncing statements.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } } } } }, "/api/extensions/softwareone/statements/{id}": { "get": { "summary": "Get SoftwareOne statement", "description": "Returns one SoftwareOne billing statement by external statement ID, including additional detail fields such as subtotal, tax amount, description, and billing address. The current handler is an MVP placeholder backed by hardcoded dummy data. The id path parameter is a SoftwareOne-style string such as stmt-001, not an Alga UUID.", "tags": [ "SoftwareOne Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-placeholder-implementation": true, "x-extension": "softwareone" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "SoftwareOne external identifier, such as agr-001 or stmt-001. This is not an Alga UUID." }, "required": true, "description": "SoftwareOne external identifier, such as agr-001 or stmt-001. This is not an Alga UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Statement detail returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneStatementDetailResponse" } } } }, "401": { "description": "API key missing at middleware before the handler executes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MiddlewareUnauthorizedResponse" } } } }, "404": { "description": "Statement not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } }, "500": { "description": "Unexpected failure while fetching statement detail.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } } } } }, "/api/extensions/softwareone/statements/{id}/charges": { "get": { "summary": "List SoftwareOne statement charges", "description": "Returns line-item charges for a SoftwareOne billing statement. The current handler is an MVP placeholder backed by hardcoded dummy data keyed by SoftwareOne statement IDs such as stmt-001. Unknown statement IDs return 200 with an empty data array rather than 404. The id path parameter is not an Alga UUID.", "tags": [ "SoftwareOne Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-placeholder-implementation": true, "x-extension": "softwareone" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "SoftwareOne external identifier, such as agr-001 or stmt-001. This is not an Alga UUID." }, "required": true, "description": "SoftwareOne external identifier, such as agr-001 or stmt-001. This is not an Alga UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Charge list returned successfully. Data may be empty when the statement ID is unknown.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneChargesListResponse" } } } }, "401": { "description": "API key missing at middleware before the handler executes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MiddlewareUnauthorizedResponse" } } } }, "500": { "description": "Unexpected failure while fetching statement charges.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } } } } }, "/api/extensions/softwareone/sync": { "post": { "summary": "Sync SoftwareOne data", "description": "Triggers the SoftwareOne aggregate sync placeholder. The current handler accepts optional syncAgreements and syncStatements booleans, waits briefly to simulate work, and returns dummy counts and a syncedAt timestamp. A full implementation will validate permissions, derive tenant context, connect to the SoftwareOne API, and persist agreement and statement data.", "tags": [ "SoftwareOne Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-placeholder-implementation": true, "x-extension": "softwareone" }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Sync options. The current sync endpoint consumes syncAgreements and syncStatements; unknown fields are ignored.", "required": false, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneSyncRequest" }, "description": "Sync options. The current sync endpoint consumes syncAgreements and syncStatements; unknown fields are ignored." } } }, "responses": { "200": { "description": "Sync placeholder completed successfully with dummy counts.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneFullSyncResponse" } } } }, "401": { "description": "API key missing at middleware before the handler executes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MiddlewareUnauthorizedResponse" } } } }, "500": { "description": "Unexpected failure while syncing SoftwareOne data.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } } } } }, "/api/extensions/{extensionId}/agreements": { "get": { "summary": "List agreements for extension", "description": "Generic extension agreements placeholder. The current handler accepts any extensionId, performs no handler-level extension validation, and returns hardcoded SoftwareOne-style agreement data with meta.extensionId echoed from the path. A full implementation will validate the extension ID, check permissions, derive tenant context, and fetch extension-specific data.", "tags": [ "SoftwareOne Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-placeholder-implementation": true, "x-extension-scoped": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Extension identifier from the URL. The MVP generic routes accept any value and echo it in response metadata." }, "required": true, "description": "Extension identifier from the URL. The MVP generic routes accept any value and echo it in response metadata.", "name": "extensionId", "in": "path" } ], "responses": { "200": { "description": "Agreement list returned successfully for the requested extension placeholder.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericExtensionAgreementsListResponse" } } } }, "401": { "description": "API key missing at middleware before the handler executes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MiddlewareUnauthorizedResponse" } } } }, "500": { "description": "Unexpected failure while fetching extension agreements.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } } } } }, "/api/extensions/{extensionId}/agreements/{id}": { "get": { "summary": "Get agreement for extension", "description": "Generic extension agreement detail placeholder. The current handler accepts any extensionId, looks up an agreement by SoftwareOne-style external ID such as agr-001, and returns hardcoded detail data with meta.extensionId echoed from the path. The id path parameter is not an Alga UUID.", "tags": [ "SoftwareOne Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-placeholder-implementation": true, "x-extension-scoped": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Extension identifier from the URL. The MVP generic routes accept any value and echo it in response metadata." }, "required": true, "description": "Extension identifier from the URL. The MVP generic routes accept any value and echo it in response metadata.", "name": "extensionId", "in": "path" }, { "schema": { "type": "string", "description": "SoftwareOne-style external record identifier, such as agr-001 or stmt-001. This is not an Alga UUID." }, "required": true, "description": "SoftwareOne-style external record identifier, such as agr-001 or stmt-001. This is not an Alga UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Agreement detail returned successfully for the requested extension placeholder.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericExtensionAgreementDetailResponse" } } } }, "401": { "description": "API key missing at middleware before the handler executes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MiddlewareUnauthorizedResponse" } } } }, "404": { "description": "Agreement not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } }, "500": { "description": "Unexpected failure while fetching extension agreement detail.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } } } } }, "/api/extensions/{extensionId}/statements": { "get": { "summary": "List statements for extension", "description": "Generic extension statements placeholder. The current handler accepts any extensionId, performs no handler-level extension validation, and returns hardcoded SoftwareOne-style statement data with meta.extensionId echoed from the path. A full implementation will validate the extension ID, check permissions, derive tenant context, and fetch extension-specific data.", "tags": [ "SoftwareOne Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-placeholder-implementation": true, "x-extension-scoped": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Extension identifier from the URL. The MVP generic routes accept any value and echo it in response metadata." }, "required": true, "description": "Extension identifier from the URL. The MVP generic routes accept any value and echo it in response metadata.", "name": "extensionId", "in": "path" } ], "responses": { "200": { "description": "Statement list returned successfully for the requested extension placeholder.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericExtensionStatementsListResponse" } } } }, "401": { "description": "API key missing at middleware before the handler executes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MiddlewareUnauthorizedResponse" } } } }, "500": { "description": "Unexpected failure while fetching extension statements.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } } } } }, "/api/extensions/{extensionId}/statements/{id}": { "get": { "summary": "Get statement for extension", "description": "Generic extension statement detail placeholder. The current handler accepts any extensionId, looks up a statement by SoftwareOne-style external ID such as stmt-001, and returns hardcoded detail data with meta.extensionId echoed from the path. The id path parameter is not an Alga UUID.", "tags": [ "SoftwareOne Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-placeholder-implementation": true, "x-extension-scoped": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Extension identifier from the URL. The MVP generic routes accept any value and echo it in response metadata." }, "required": true, "description": "Extension identifier from the URL. The MVP generic routes accept any value and echo it in response metadata.", "name": "extensionId", "in": "path" }, { "schema": { "type": "string", "description": "SoftwareOne-style external record identifier, such as agr-001 or stmt-001. This is not an Alga UUID." }, "required": true, "description": "SoftwareOne-style external record identifier, such as agr-001 or stmt-001. This is not an Alga UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Statement detail returned successfully for the requested extension placeholder.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericExtensionStatementDetailResponse" } } } }, "401": { "description": "API key missing at middleware before the handler executes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MiddlewareUnauthorizedResponse" } } } }, "404": { "description": "Statement not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } }, "500": { "description": "Unexpected failure while fetching extension statement detail.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } } } } }, "/api/extensions/{extensionId}/statements/{id}/charges": { "get": { "summary": "List charges for extension statement", "description": "Generic extension statement charges placeholder. The current handler accepts any extensionId, returns hardcoded charge data keyed by SoftwareOne-style statement IDs such as stmt-001, and echoes extensionId in response metadata. Unknown statement IDs return 200 with an empty data array rather than 404.", "tags": [ "SoftwareOne Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-placeholder-implementation": true, "x-extension-scoped": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Extension identifier from the URL. The MVP generic routes accept any value and echo it in response metadata." }, "required": true, "description": "Extension identifier from the URL. The MVP generic routes accept any value and echo it in response metadata.", "name": "extensionId", "in": "path" }, { "schema": { "type": "string", "description": "SoftwareOne-style external record identifier, such as agr-001 or stmt-001. This is not an Alga UUID." }, "required": true, "description": "SoftwareOne-style external record identifier, such as agr-001 or stmt-001. This is not an Alga UUID.", "name": "id", "in": "path" } ], "responses": { "200": { "description": "Charge list returned successfully. Data may be empty when the statement ID is unknown.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericExtensionChargesListResponse" } } } }, "401": { "description": "API key missing at middleware before the handler executes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MiddlewareUnauthorizedResponse" } } } }, "500": { "description": "Unexpected failure while fetching extension statement charges.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } } } } }, "/api/extensions/{extensionId}/sync": { "post": { "summary": "Sync extension data", "description": "Generic extension sync placeholder. The current handler accepts any extensionId and optional syncAgreements and syncStatements booleans, simulates work, and returns dummy agreement and statement counts with meta extensionId in the response data. A full implementation will validate extension identity, check permissions, derive tenant context, call the extension-specific backend, and persist data.", "tags": [ "SoftwareOne Extensions" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-placeholder-implementation": true, "x-extension-scoped": true }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "Extension identifier from the URL. The MVP generic routes accept any value and echo it in response metadata." }, "required": true, "description": "Extension identifier from the URL. The MVP generic routes accept any value and echo it in response metadata.", "name": "extensionId", "in": "path" } ], "requestBody": { "description": "Optional sync flags consumed by the MVP handler: syncAgreements and syncStatements.", "required": false, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneSyncRequest" }, "description": "Optional sync flags consumed by the MVP handler: syncAgreements and syncStatements." } } }, "responses": { "200": { "description": "Generic extension sync placeholder completed successfully with dummy counts.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericExtensionSyncResponse" } } } }, "401": { "description": "API key missing at middleware before the handler executes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MiddlewareUnauthorizedResponse" } } } }, "500": { "description": "Unexpected failure while syncing extension data.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SoftwareOneErrorResponse" } } } } } } }, "/api/accounting/csv/export": { "post": { "summary": "POST accounting", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "accounting" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/accounting/csv/import/tax": { "post": { "summary": "POST accounting", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "accounting" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/accounting/csv/import/tax/history": { "get": { "summary": "GET accounting", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "accounting" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/accounting/csv/import/tax/rollback/{importId}": { "post": { "summary": "POST accounting", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "accounting" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/accounting/csv/import/tax/template": { "get": { "summary": "GET accounting", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "accounting" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/accounting/exports": { "get": { "summary": "GET accounting", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "accounting" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST accounting", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "accounting" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/accounting/exports/locks/invoice/reset": { "post": { "summary": "POST accounting", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "accounting" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/accounting/exports/preview": { "post": { "summary": "POST accounting", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "accounting" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/accounting/exports/{batchId}": { "get": { "summary": "GET accounting", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "accounting" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "patch": { "summary": "PATCH accounting", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "accounting" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/accounting/exports/{batchId}/download": { "post": { "summary": "POST accounting", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "accounting" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/accounting/exports/{batchId}/errors": { "post": { "summary": "POST accounting", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "accounting" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/accounting/exports/{batchId}/execute": { "post": { "summary": "POST accounting", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "accounting" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/accounting/exports/{batchId}/lines": { "post": { "summary": "POST accounting", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "accounting" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/auth/e2e/google/authorize": { "get": { "summary": "GET auth", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "auth" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/auth/e2e/google/complete": { "get": { "summary": "GET auth", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "auth" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/auth/e2e/google/token": { "post": { "summary": "POST auth", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "auth" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/auth/e2e/google/userinfo": { "get": { "summary": "GET auth", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "auth" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/auth/google/calendar/callback": { "get": { "summary": "GET auth", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "auth" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/auth/microsoft/calendar/callback": { "get": { "summary": "GET auth", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "auth" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/auth/microsoft/entra/callback": { "get": { "summary": "GET auth", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "auth" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/auth/msp/remember-email": { "post": { "summary": "POST auth", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "auth" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/auth/msp/sso/discover": { "post": { "summary": "POST auth", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "auth" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/auth/msp/sso/resolve": { "post": { "summary": "POST auth", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "auth" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/auth/sessions": { "delete": { "summary": "DELETE auth", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "auth" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "204": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "GET auth", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "auth" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/auth/sessions/all": { "get": { "summary": "GET auth", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "auth" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/auth/sessions/{sessionId}": { "delete": { "summary": "DELETE auth", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "auth" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "204": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/billing/check-tenant": { "get": { "summary": "GET billing", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "billing" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/calendar/appointment/{id}": { "get": { "summary": "GET calendar", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "calendar" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/calendar/webhooks/google": { "get": { "summary": "GET calendar", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "calendar" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS calendar", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "calendar" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST calendar", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "calendar" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/calendar/webhooks/microsoft": { "get": { "summary": "GET calendar", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "calendar" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS calendar", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "calendar" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST calendar", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "calendar" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/chat/v1/completions": { "post": { "summary": "POST chat", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "chat" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/chat/v1/completions/stream": { "post": { "summary": "POST chat", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "chat" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/chat/v1/execute": { "post": { "summary": "POST chat", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "chat" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/client-portal/domain-session": { "options": { "summary": "OPTIONS client-portal", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "client-portal" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST client-portal", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "client-portal" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/clients": { "get": { "summary": "GET clients", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "clients" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/documents/{documentId}/content": { "get": { "summary": "GET documents", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "documents" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/documents/{documentId}/preview": { "get": { "summary": "GET documents", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "documents" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/documents/{documentId}/thumbnail": { "get": { "summary": "GET documents", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "documents" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/email/imap/reconnect": { "post": { "summary": "POST email", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "email" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/email/imap/resync": { "post": { "summary": "POST email", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "email" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/email/oauth/imap/callback": { "get": { "summary": "GET email", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "email" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa", "algadesk" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/email/oauth/imap/initiate": { "post": { "summary": "POST email", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "email" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa", "algadesk" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/email/webhooks/imap": { "post": { "summary": "POST email", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "email" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/email/webhooks/resend": { "post": { "summary": "POST email", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "email" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/ext-bundles/abort": { "post": { "summary": "POST ext-bundles", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "ext-bundles" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/ext-bundles/finalize": { "post": { "summary": "POST ext-bundles", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "ext-bundles" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/ext-bundles/upload-proxy": { "post": { "summary": "POST ext-bundles", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "ext-bundles" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/ext-debug/stream": { "get": { "summary": "GET ext-debug", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "ext-debug" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST ext-debug", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "ext-debug" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/ext-proxy/{extensionId}/{path}": { "delete": { "summary": "DELETE ext-proxy", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "ext-proxy" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "204": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "GET ext-proxy", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "ext-proxy" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "patch": { "summary": "PATCH ext-proxy", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "ext-proxy" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST ext-proxy", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "ext-proxy" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "put": { "summary": "PUT ext-proxy", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "ext-proxy" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/import/approve": { "post": { "summary": "POST import", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "import" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/import/details": { "get": { "summary": "GET import", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "import" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/import/history": { "get": { "summary": "GET import", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "import" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/import/mapping": { "get": { "summary": "GET import", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "import" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/import/preview": { "post": { "summary": "POST import", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "import" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/import/sources": { "get": { "summary": "GET import", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "import" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/entra": { "get": { "summary": "GET integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/entra/connect": { "options": { "summary": "OPTIONS integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/entra/disconnect": { "options": { "summary": "OPTIONS integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/entra/discovery": { "options": { "summary": "OPTIONS integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/entra/mappings/confirm": { "options": { "summary": "OPTIONS integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/entra/mappings/preview": { "get": { "summary": "GET integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/entra/mappings/remap": { "options": { "summary": "OPTIONS integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/entra/mappings/unmap": { "options": { "summary": "OPTIONS integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/entra/sync": { "options": { "summary": "OPTIONS integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/entra/sync/runs": { "get": { "summary": "GET integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/entra/sync/runs/{runId}": { "get": { "summary": "GET integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/entra/validate-cipp": { "options": { "summary": "OPTIONS integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/entra/validate-direct": { "options": { "summary": "OPTIONS integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/ninjaone/callback": { "get": { "summary": "GET integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/xero/callback": { "get": { "summary": "GET integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/integrations/xero/connect": { "get": { "summary": "GET integrations", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "integrations" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/internal/check-tenant-email": { "post": { "summary": "POST internal", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "internal" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/internal/collab/persist": { "post": { "summary": "POST internal", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "internal" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/internal/ext-clients/install/{installId}": { "post": { "summary": "POST internal", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "internal" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/internal/ext-invoicing/install/{installId}": { "post": { "summary": "POST internal", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "internal" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/internal/ext-runner/install-config": { "post": { "summary": "POST internal", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "internal" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/internal/ext-scheduler/install/{installId}": { "post": { "summary": "POST internal", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "internal" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/internal/ext-services/install/{installId}": { "post": { "summary": "POST internal", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "internal" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/internal/ext-storage/install/{installId}": { "post": { "summary": "POST internal", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "internal" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/online-meetings/recordings/{artifactId}": { "get": { "summary": "GET online-meetings", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "online-meetings" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/public/appointment-request": { "post": { "summary": "POST public", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "public" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/public/appointment-request/available-dates": { "get": { "summary": "GET public", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "public" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/public/appointment-request/available-services": { "get": { "summary": "GET public", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "public" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/public/appointment-request/available-slots": { "get": { "summary": "GET public", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "public" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/secrets": { "get": { "summary": "GET secrets", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "secrets" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST secrets", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "secrets" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/secrets/{name}": { "delete": { "summary": "DELETE secrets", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "secrets" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "204": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "GET secrets", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "secrets" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "head": { "summary": "HEAD secrets", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "secrets" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "patch": { "summary": "PATCH secrets", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "secrets" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/share/{token}": { "get": { "summary": "GET share", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "share" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/share/{token}/info": { "get": { "summary": "GET share", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "share" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/teams/auth/callback/bot": { "get": { "summary": "GET teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/teams/auth/callback/message-extension": { "get": { "summary": "GET teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/teams/auth/callback/tab": { "get": { "summary": "GET teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/teams/bot/messages": { "options": { "summary": "OPTIONS teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/teams/message-extension/query": { "options": { "summary": "OPTIONS teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/teams/package": { "get": { "summary": "GET teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/teams/package/download": { "get": { "summary": "GET teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/teams/quick-actions": { "options": { "summary": "OPTIONS teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/teams/webhooks/recordings": { "get": { "summary": "GET teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST teams", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "teams" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/tickets/{id}/live-token": { "get": { "summary": "GET tickets", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "tickets" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/account/delete": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/auth/apple": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/auth/apple/link": { "delete": { "summary": "DELETE v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "204": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/auth/apple/notifications": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/auth/capabilities": { "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/auth/exchange": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/auth/refresh": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/auth/revoke": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/iap/check-email": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/iap/notifications": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/iap/provision": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/iap/restore": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/moderation/mutes": { "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/moderation/mutes/{userId}": { "delete": { "summary": "DELETE v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "204": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/moderation/report": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/mobile/push-token": { "delete": { "summary": "DELETE v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "204": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "put": { "summary": "PUT v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "mobile" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/platform-feature-flags": { "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-feature-flags" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-feature-flags" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-feature-flags" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/platform-feature-flags/{flagId}": { "delete": { "summary": "DELETE v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-feature-flags" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "204": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-feature-flags" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-feature-flags" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "patch": { "summary": "PATCH v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-feature-flags" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-feature-flags" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/platform-feature-flags/{flagId}/tenants": { "options": { "summary": "OPTIONS v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-feature-flags" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-feature-flags" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/platform-notifications": { "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-notifications" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-notifications" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-notifications" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/platform-notifications/resolve-recipients": { "options": { "summary": "OPTIONS v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-notifications" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-notifications" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/platform-notifications/{notificationId}": { "delete": { "summary": "DELETE v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-notifications" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "204": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-notifications" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-notifications" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-notifications" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "put": { "summary": "PUT v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-notifications" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/platform-notifications/{notificationId}/reads": { "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-notifications" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-notifications" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/platform-notifications/{notificationId}/stats": { "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-notifications" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-notifications" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/platform-reports": { "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/platform-reports/access": { "options": { "summary": "OPTIONS v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/platform-reports/audit": { "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/platform-reports/schema": { "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/platform-reports/{reportId}": { "delete": { "summary": "DELETE v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "204": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "put": { "summary": "PUT v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/platform-reports/{reportId}/execute": { "options": { "summary": "OPTIONS v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "platform-reports" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/storage/namespaces/{namespace}/records": { "get": { "summary": "List records in a namespace", "tags": [ "Storage" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "storage", "x-chat-callable": true, "x-chat-display-name": "List Storage Records", "x-chat-rbac-resource": "storage", "x-chat-approval-required": false, "x-edition": "CE" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "minLength": 1, "maxLength": 128 }, "required": true, "name": "namespace", "in": "path" }, { "schema": { "type": "integer", "minimum": 1, "maximum": 100 }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "cursor", "in": "query" }, { "schema": { "type": "string", "maxLength": 256 }, "required": false, "name": "keyPrefix", "in": "query" }, { "schema": { "type": "boolean" }, "required": false, "name": "includeValues", "in": "query" }, { "schema": { "type": "boolean" }, "required": false, "name": "includeMetadata", "in": "query" } ], "responses": { "200": { "description": "List of records", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StorageListResponse" } } } }, "400": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "Bulk insert or update records", "tags": [ "Storage" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "storage", "x-chat-callable": true, "x-chat-display-name": "Upsert Storage Records", "x-chat-rbac-resource": "storage", "x-chat-approval-required": true, "x-edition": "CE" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "minLength": 1, "maxLength": 128 }, "required": true, "name": "namespace", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StorageBulkPutRequest" } } } }, "responses": { "200": { "description": "Bulk operation result", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StorageBulkPutResponse" } } } }, "400": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "409": { "description": "Revision mismatch", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "429": { "description": "Quota exceeded", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/storage/namespaces/{namespace}/records/{key}": { "delete": { "summary": "Delete a record by key", "tags": [ "Storage" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "storage", "x-edition": "CE" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "minLength": 1, "maxLength": 128 }, "required": true, "name": "namespace", "in": "path" }, { "schema": { "type": "string", "minLength": 1, "maxLength": 256 }, "required": true, "name": "key", "in": "path" }, { "schema": { "type": [ "integer", "null" ], "minimum": 0 }, "required": false, "name": "ifRevision", "in": "query" } ], "responses": { "204": { "description": "Record deleted" }, "400": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "Get a record by key", "tags": [ "Storage" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "storage", "x-chat-callable": true, "x-chat-display-name": "Get Storage Record", "x-chat-rbac-resource": "storage", "x-chat-approval-required": false, "x-edition": "CE" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "minLength": 1, "maxLength": 128 }, "required": true, "name": "namespace", "in": "path" }, { "schema": { "type": "string", "minLength": 1, "maxLength": 256 }, "required": true, "name": "key", "in": "path" }, { "schema": { "type": "string" }, "required": false, "name": "if-revision-match", "in": "header" } ], "responses": { "200": { "description": "Record", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StorageGetResponse" } } } }, "400": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "409": { "description": "Revision mismatch", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "put": { "summary": "Create or update a record by key", "tags": [ "Storage" ], "security": [ { "ApiKeyAuth": [] } ], "extensions": { "x-tenant-header-required": true, "x-rbac-resource": "storage", "x-chat-callable": true, "x-chat-display-name": "Put Storage Record", "x-chat-rbac-resource": "storage", "x-chat-approval-required": true, "x-edition": "CE" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "minLength": 1, "maxLength": 128 }, "required": true, "name": "namespace", "in": "path" }, { "schema": { "type": "string", "minLength": 1, "maxLength": 256 }, "required": true, "name": "key", "in": "path" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StoragePutRequest" } } } }, "responses": { "200": { "description": "Updated record", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StoragePutResponse" } } } }, "400": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "409": { "description": "Revision mismatch", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "429": { "description": "Quota exceeded", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/tenant-management/addons": { "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "tenant-management" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/tenant-management/audit": { "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "tenant-management" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/tenant-management/confirm-deletion": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "tenant-management" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/tenant-management/create-tenant": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "tenant-management" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/tenant-management/export-tenant": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "tenant-management" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/tenant-management/exports": { "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "tenant-management" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/tenant-management/exports/{exportId}/download-url": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "tenant-management" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/tenant-management/pending-deletions": { "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "tenant-management" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/tenant-management/resend-welcome-email": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "tenant-management" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/tenant-management/rollback-deletion": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "tenant-management" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/tenant-management/start-deletion": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "tenant-management" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/tenant-management/start-premium-trial": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "tenant-management" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/tenant-management/tenants": { "get": { "summary": "GET v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "tenant-management" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/v1/tenant-management/tenants/{tenantId}/addons": { "post": { "summary": "POST v1", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "tenant-management" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/webhooks/ninjaone": { "get": { "summary": "GET webhooks", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "webhooks" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "options": { "summary": "OPTIONS webhooks", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "webhooks" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST webhooks", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "webhooks" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/webhooks/stripe": { "post": { "summary": "POST webhooks", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "webhooks" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/webhooks/stripe/payments": { "post": { "summary": "POST webhooks", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "webhooks" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/api/webhooks/tacticalrmm": { "options": { "summary": "OPTIONS webhooks", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "webhooks" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "responses": { "200": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "POST webhooks", "description": "This operation was generated automatically from the route inventory. Replace with canonical OpenAPI metadata.", "tags": [ "webhooks" ], "extensions": { "x-generated-from": "docs/openapi/route-inventory.json", "x-placeholder": true }, "x-alga-products": [ "psa" ], "requestBody": { "description": "Placeholder request body", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" }, "description": "Placeholder request body" } } }, "responses": { "201": { "description": "Placeholder success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceholderObject" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } } }, "webhooks": { "ticket.created": { "post": { "summary": "Outbound delivery: ticket.created", "description": "Emitted when a ticket is first created. `data.changes` and `data.comment` are not included on this event. Delivered with HMAC-SHA256 signature header X-Alga-Signature, and the X-Alga-* metadata headers. Retried with backoff (1m, 5m, 30m, 2h, 12h) before being abandoned.", "tags": [ "Webhooks" ], "extensions": { "x-alga-event-type": "ticket.created", "x-rbac-resource": "webhook" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "`t=,v1=` over `${timestamp}.${raw_body}`." }, "required": true, "description": "`t=,v1=` over `${timestamp}.${raw_body}`.", "name": "x-alga-signature", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-webhook-id", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-event-id", "in": "header" }, { "schema": { "$ref": "#/components/schemas/WebhookEventTypeV1" }, "required": true, "name": "x-alga-event-type", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-delivery-id", "in": "header" }, { "schema": { "type": "string", "description": "Stringified attempt count, starts at 1." }, "required": true, "description": "Stringified attempt count, starts at 1.", "name": "x-alga-delivery-attempt", "in": "header" } ], "requestBody": { "description": "Signed JSON envelope.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TicketCreatedDeliveryV1" }, "description": "Signed JSON envelope." } } }, "responses": { "200": { "description": "Endpoint accepted the delivery (any 2xx is treated as success)." }, "410": { "description": "Endpoint indicates the resource is permanently gone; webhook will be auto-disabled." } } } }, "ticket.updated": { "post": { "summary": "Outbound delivery: ticket.updated", "description": "Emitted when a ticket is updated. `data.changes` is a record keyed by changed field name with `{previous, new}` entries. Delivered with HMAC-SHA256 signature header X-Alga-Signature, and the X-Alga-* metadata headers. Retried with backoff (1m, 5m, 30m, 2h, 12h) before being abandoned.", "tags": [ "Webhooks" ], "extensions": { "x-alga-event-type": "ticket.updated", "x-rbac-resource": "webhook" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "`t=,v1=` over `${timestamp}.${raw_body}`." }, "required": true, "description": "`t=,v1=` over `${timestamp}.${raw_body}`.", "name": "x-alga-signature", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-webhook-id", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-event-id", "in": "header" }, { "schema": { "$ref": "#/components/schemas/WebhookEventTypeV1" }, "required": true, "name": "x-alga-event-type", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-delivery-id", "in": "header" }, { "schema": { "type": "string", "description": "Stringified attempt count, starts at 1." }, "required": true, "description": "Stringified attempt count, starts at 1.", "name": "x-alga-delivery-attempt", "in": "header" } ], "requestBody": { "description": "Signed JSON envelope.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TicketUpdatedDeliveryV1" }, "description": "Signed JSON envelope." } } }, "responses": { "200": { "description": "Endpoint accepted the delivery (any 2xx is treated as success)." }, "410": { "description": "Endpoint indicates the resource is permanently gone; webhook will be auto-disabled." } } } }, "ticket.status_changed": { "post": { "summary": "Outbound delivery: ticket.status_changed", "description": "Emitted when a ticket status changes. `data.previous_status_id` / `previous_status_name` are populated. Delivered with HMAC-SHA256 signature header X-Alga-Signature, and the X-Alga-* metadata headers. Retried with backoff (1m, 5m, 30m, 2h, 12h) before being abandoned.", "tags": [ "Webhooks" ], "extensions": { "x-alga-event-type": "ticket.status_changed", "x-rbac-resource": "webhook" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "`t=,v1=` over `${timestamp}.${raw_body}`." }, "required": true, "description": "`t=,v1=` over `${timestamp}.${raw_body}`.", "name": "x-alga-signature", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-webhook-id", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-event-id", "in": "header" }, { "schema": { "$ref": "#/components/schemas/WebhookEventTypeV1" }, "required": true, "name": "x-alga-event-type", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-delivery-id", "in": "header" }, { "schema": { "type": "string", "description": "Stringified attempt count, starts at 1." }, "required": true, "description": "Stringified attempt count, starts at 1.", "name": "x-alga-delivery-attempt", "in": "header" } ], "requestBody": { "description": "Signed JSON envelope.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TicketStatusChangedDeliveryV1" }, "description": "Signed JSON envelope." } } }, "responses": { "200": { "description": "Endpoint accepted the delivery (any 2xx is treated as success)." }, "410": { "description": "Endpoint indicates the resource is permanently gone; webhook will be auto-disabled." } } } }, "ticket.assigned": { "post": { "summary": "Outbound delivery: ticket.assigned", "description": "Emitted when a ticket assignment changes (assigned_to or assigned_team_id). Delivered with HMAC-SHA256 signature header X-Alga-Signature, and the X-Alga-* metadata headers. Retried with backoff (1m, 5m, 30m, 2h, 12h) before being abandoned.", "tags": [ "Webhooks" ], "extensions": { "x-alga-event-type": "ticket.assigned", "x-rbac-resource": "webhook" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "`t=,v1=` over `${timestamp}.${raw_body}`." }, "required": true, "description": "`t=,v1=` over `${timestamp}.${raw_body}`.", "name": "x-alga-signature", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-webhook-id", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-event-id", "in": "header" }, { "schema": { "$ref": "#/components/schemas/WebhookEventTypeV1" }, "required": true, "name": "x-alga-event-type", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-delivery-id", "in": "header" }, { "schema": { "type": "string", "description": "Stringified attempt count, starts at 1." }, "required": true, "description": "Stringified attempt count, starts at 1.", "name": "x-alga-delivery-attempt", "in": "header" } ], "requestBody": { "description": "Signed JSON envelope.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TicketAssignedDeliveryV1" }, "description": "Signed JSON envelope." } } }, "responses": { "200": { "description": "Endpoint accepted the delivery (any 2xx is treated as success)." }, "410": { "description": "Endpoint indicates the resource is permanently gone; webhook will be auto-disabled." } } } }, "ticket.closed": { "post": { "summary": "Outbound delivery: ticket.closed", "description": "Emitted when a ticket transitions to a closed status. `data.is_closed` is true and `data.closed_at` is set. Delivered with HMAC-SHA256 signature header X-Alga-Signature, and the X-Alga-* metadata headers. Retried with backoff (1m, 5m, 30m, 2h, 12h) before being abandoned.", "tags": [ "Webhooks" ], "extensions": { "x-alga-event-type": "ticket.closed", "x-rbac-resource": "webhook" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "`t=,v1=` over `${timestamp}.${raw_body}`." }, "required": true, "description": "`t=,v1=` over `${timestamp}.${raw_body}`.", "name": "x-alga-signature", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-webhook-id", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-event-id", "in": "header" }, { "schema": { "$ref": "#/components/schemas/WebhookEventTypeV1" }, "required": true, "name": "x-alga-event-type", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-delivery-id", "in": "header" }, { "schema": { "type": "string", "description": "Stringified attempt count, starts at 1." }, "required": true, "description": "Stringified attempt count, starts at 1.", "name": "x-alga-delivery-attempt", "in": "header" } ], "requestBody": { "description": "Signed JSON envelope.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TicketClosedDeliveryV1" }, "description": "Signed JSON envelope." } } }, "responses": { "200": { "description": "Endpoint accepted the delivery (any 2xx is treated as success)." }, "410": { "description": "Endpoint indicates the resource is permanently gone; webhook will be auto-disabled." } } } }, "ticket.comment.added": { "post": { "summary": "Outbound delivery: ticket.comment.added", "description": "Emitted when a comment is added to a ticket. `data.comment` carries the new comment block; attachments are never included. Delivered with HMAC-SHA256 signature header X-Alga-Signature, and the X-Alga-* metadata headers. Retried with backoff (1m, 5m, 30m, 2h, 12h) before being abandoned.", "tags": [ "Webhooks" ], "extensions": { "x-alga-event-type": "ticket.comment.added", "x-rbac-resource": "webhook" }, "x-alga-products": [ "psa" ], "parameters": [ { "schema": { "type": "string", "description": "`t=,v1=` over `${timestamp}.${raw_body}`." }, "required": true, "description": "`t=,v1=` over `${timestamp}.${raw_body}`.", "name": "x-alga-signature", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-webhook-id", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-event-id", "in": "header" }, { "schema": { "$ref": "#/components/schemas/WebhookEventTypeV1" }, "required": true, "name": "x-alga-event-type", "in": "header" }, { "schema": { "type": "string", "format": "uuid" }, "required": true, "name": "x-alga-delivery-id", "in": "header" }, { "schema": { "type": "string", "description": "Stringified attempt count, starts at 1." }, "required": true, "description": "Stringified attempt count, starts at 1.", "name": "x-alga-delivery-attempt", "in": "header" } ], "requestBody": { "description": "Signed JSON envelope.", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TicketCommentAddedDeliveryV1" }, "description": "Signed JSON envelope." } } }, "responses": { "200": { "description": "Endpoint accepted the delivery (any 2xx is treated as success)." }, "410": { "description": "Endpoint indicates the resource is permanently gone; webhook will be auto-disabled." } } } } } }