openapi: 3.1.0 info: title: Primora API version: 0.2.14 description: Core Primora platform API for tenancy, storage, API keys, and audit operations. servers: - url: /api/v1 tags: - name: Health - name: Platform - name: Organizations - name: Projects - name: Storage - name: Collections - name: Databases - name: Telemetry - name: Integrations - name: Webhooks - name: Automation - name: Instance paths: /health/liveness: get: tags: [Health] operationId: getLiveness responses: "200": description: Liveness check content: application/json: schema: $ref: "#/components/schemas/HealthStatus" /health/readiness: get: tags: [Health] operationId: getReadiness responses: "200": description: Readiness check content: application/json: schema: $ref: "#/components/schemas/ReadinessStatus" /metrics: get: tags: [Health] operationId: getMetrics summary: Prometheus text exposition of platform HTTP metrics responses: "200": description: Prometheus metrics content: text/plain: schema: type: string /me: get: tags: [Platform] operationId: getMe security: - bearerAuth: [] responses: "200": description: Current authenticated user and tenancy summary content: application/json: schema: $ref: "#/components/schemas/MeResponse" "401": $ref: "#/components/responses/ErrorResponse" /instance/public: get: tags: [Instance] operationId: getInstancePublic summary: Public instance state for the login page — no auth required responses: "200": description: Public instance configuration content: application/json: schema: $ref: "#/components/schemas/InstancePublicConfig" /instance/settings: get: tags: [Instance] operationId: listInstanceSettings summary: List instance settings (platform admin only) security: - bearerAuth: [] responses: "200": description: Resolved instance settings content: application/json: schema: $ref: "#/components/schemas/InstanceSettingsResponse" "401": $ref: "#/components/responses/ErrorResponse" "403": $ref: "#/components/responses/ErrorResponse" /instance/settings/{key}: put: tags: [Instance] operationId: updateInstanceSetting summary: Set an instance setting override (platform admin only) security: - bearerAuth: [] parameters: - name: key in: path required: true schema: type: string requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateInstanceSettingRequest" responses: "200": description: Setting stored content: application/json: schema: $ref: "#/components/schemas/InstanceSetting" "400": $ref: "#/components/responses/ErrorResponse" "403": $ref: "#/components/responses/ErrorResponse" "404": $ref: "#/components/responses/ErrorResponse" delete: tags: [Instance] operationId: deleteInstanceSetting summary: Revert a setting to its env/default value (platform admin only) security: - bearerAuth: [] parameters: - name: key in: path required: true schema: type: string responses: "204": description: Setting reverted "403": $ref: "#/components/responses/ErrorResponse" "404": $ref: "#/components/responses/ErrorResponse" /bootstrap: post: tags: [Platform] operationId: bootstrapPlatform security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/BootstrapRequest" responses: "201": description: Platform bootstrapped content: application/json: schema: $ref: "#/components/schemas/BootstrapResponse" "409": $ref: "#/components/responses/ErrorResponse" /organizations: get: tags: [Organizations] operationId: listOrganizations security: - bearerAuth: [] responses: "200": description: Organizations for the current user content: application/json: schema: type: object properties: items: type: array items: $ref: "#/components/schemas/OrganizationMembership" required: [items] post: tags: [Organizations] operationId: createOrganization security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateOrganizationRequest" responses: "201": description: Organization created content: application/json: schema: $ref: "#/components/schemas/OrganizationMembership" /organizations/{organizationID}: patch: tags: [Organizations] operationId: updateOrganization security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/OrganizationID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateOrganizationRequest" responses: "204": description: Organization updated delete: tags: [Organizations] operationId: deleteOrganization security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/OrganizationID" responses: "204": description: Organization deleted /organizations/{organizationID}/members: get: tags: [Organizations] operationId: listOrganizationMembers security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/OrganizationID" responses: "200": description: Members of an organization content: application/json: schema: type: object properties: items: type: array items: $ref: "#/components/schemas/OrganizationMember" required: [items] /organizations/{organizationID}/members/{userID}: patch: tags: [Organizations] operationId: updateOrganizationMemberRole security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/OrganizationID" - $ref: "#/components/parameters/UserID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateOrganizationMemberRoleRequest" responses: "200": description: Organization member role updated content: application/json: schema: $ref: "#/components/schemas/OrganizationMember" delete: tags: [Organizations] operationId: removeOrganizationMember security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/OrganizationID" - $ref: "#/components/parameters/UserID" responses: "204": description: Organization member removed /organizations/{organizationID}/projects: get: tags: [Projects] operationId: listProjects security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/OrganizationID" - $ref: "#/components/parameters/AuditQuery" responses: "200": description: Projects within an organization content: application/json: schema: type: object properties: items: type: array items: $ref: "#/components/schemas/Project" required: [items] post: tags: [Projects] operationId: createProject security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/OrganizationID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateProjectRequest" responses: "201": description: Project created content: application/json: schema: $ref: "#/components/schemas/Project" /organizations/{organizationID}/invitations: get: tags: [Organizations] operationId: listOrganizationInvitations security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/OrganizationID" responses: "200": description: Invitations for an organization content: application/json: schema: type: object properties: items: type: array items: $ref: "#/components/schemas/OrganizationInvitation" required: [items] post: tags: [Organizations] operationId: createInvitation security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/OrganizationID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateInvitationRequest" responses: "201": description: Invitation created content: application/json: schema: $ref: "#/components/schemas/Invitation" /organizations/{organizationID}/invitations/{invitationID}: delete: tags: [Organizations] operationId: revokeInvitation security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/OrganizationID" - $ref: "#/components/parameters/InvitationID" responses: "204": description: Invitation revoked /invitations/accept: post: tags: [Organizations] operationId: acceptInvitation security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/AcceptInvitationRequest" responses: "204": description: Invitation accepted /projects/{projectID}/overview: get: tags: [Projects] operationId: getProjectOverview security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" responses: "200": description: Aggregated project overview metrics content: application/json: schema: $ref: "#/components/schemas/ProjectOverview" /projects/{projectID}/api-keys: get: tags: [Projects] operationId: listApiKeys security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" responses: "200": description: API keys for a project content: application/json: schema: type: object properties: items: type: array items: $ref: "#/components/schemas/ApiKey" required: [items] post: tags: [Projects] operationId: createApiKey security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateApiKeyRequest" responses: "201": description: API key created content: application/json: schema: $ref: "#/components/schemas/CreatedApiKey" /projects/{projectID}: patch: tags: [Projects] operationId: updateProject security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateProjectRequest" responses: "204": description: Project updated delete: tags: [Projects] operationId: deleteProject security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" responses: "204": description: Project deleted /projects/{projectID}/members: get: tags: [Projects] operationId: listProjectMembers security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" responses: "200": description: Members of a project content: application/json: schema: type: object properties: items: type: array items: $ref: "#/components/schemas/ProjectMember" required: [items] /projects/{projectID}/members/{userID}: patch: tags: [Projects] operationId: updateProjectMemberRole security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/UserID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateProjectMemberRoleRequest" responses: "200": description: Project member role updated content: application/json: schema: $ref: "#/components/schemas/ProjectMember" delete: tags: [Projects] operationId: removeProjectMember security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/UserID" responses: "204": description: Project member removed /projects/{projectID}/api-keys/{apiKeyID}: delete: tags: [Projects] operationId: revokeApiKey security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/ApiKeyID" responses: "204": description: API key revoked /projects/{projectID}/buckets: get: tags: [Storage] operationId: listBuckets security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/AuditQuery" responses: "200": description: Buckets for a project content: application/json: schema: type: object properties: items: type: array items: $ref: "#/components/schemas/Bucket" required: [items] post: tags: [Storage] operationId: createBucket security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateBucketRequest" responses: "201": description: Bucket created content: application/json: schema: $ref: "#/components/schemas/Bucket" /projects/{projectID}/audit-logs: get: tags: [Projects] operationId: listAuditLogs security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/AuditQuery" - $ref: "#/components/parameters/AuditAction" - $ref: "#/components/parameters/PaginationLimit" - $ref: "#/components/parameters/PaginationOffset" responses: "200": description: Audit logs for a project content: application/json: schema: $ref: "#/components/schemas/AuditLogListResponse" /buckets/{bucketID}/objects: get: tags: [Storage] operationId: listBucketObjects security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/BucketID" - $ref: "#/components/parameters/AuditQuery" - $ref: "#/components/parameters/PaginationLimit" - $ref: "#/components/parameters/PaginationOffset" responses: "200": description: Bucket objects content: application/json: schema: $ref: "#/components/schemas/BucketObjectListResponse" post: tags: [Storage] operationId: uploadBucketObject security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/BucketID" requestBody: required: true content: multipart/form-data: schema: type: object properties: objectKey: type: string file: type: string format: binary required: [file] responses: "201": description: Bucket object created content: application/json: schema: $ref: "#/components/schemas/BucketObject" /buckets/{bucketID}/object-copies: post: tags: [Storage] operationId: copyBucketObject security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/BucketID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CopyBucketObjectRequest" responses: "201": description: Bucket object copied content: application/json: schema: $ref: "#/components/schemas/BucketObject" /buckets/{bucketID}/object-presigns: post: tags: [Storage] operationId: presignBucketObject description: >- Mint a short-lived URL the client calls against object storage directly — keeps large transfers off the backend. Requires BACKEND_STORAGE_DRIVER=s3; the URL's host comes from S3_PUBLIC_ENDPOINT (or S3_ENDPOINT when unset). Signatures bind the host — a presigned URL is valid only at the endpoint it was minted for. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/BucketID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/PresignObjectRequest" responses: "200": description: Presigned URL content: application/json: schema: $ref: "#/components/schemas/PresignedURL" "404": description: Bucket or object not found (download) /buckets/{bucketID}: patch: tags: [Storage] operationId: updateBucket security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/BucketID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateBucketRequest" responses: "204": description: Bucket updated delete: tags: [Storage] operationId: deleteBucket security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/BucketID" responses: "204": description: Bucket deleted /buckets/{bucketID}/objects/{objectKey}: get: tags: [Storage] operationId: downloadBucketObject security: - bearerAuth: [] - apiKeyAuth: [] - {} parameters: - $ref: "#/components/parameters/BucketID" - $ref: "#/components/parameters/ObjectKey" responses: "200": description: Bucket object bytes content: application/octet-stream: schema: type: string format: binary patch: tags: [Storage] operationId: updateBucketObject security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/BucketID" - $ref: "#/components/parameters/ObjectKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateBucketObjectRequest" responses: "204": description: Bucket object updated delete: tags: [Storage] operationId: deleteBucketObject security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/BucketID" - $ref: "#/components/parameters/ObjectKey" responses: "204": description: Bucket object deleted /projects/{projectID}/collections: get: tags: [Collections] operationId: listCollections security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" responses: "200": description: List of collections in the project content: application/json: schema: $ref: "#/components/schemas/CollectionListResponse" post: tags: [Collections] operationId: createCollection security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateCollectionRequest" responses: "201": description: Collection created content: application/json: schema: $ref: "#/components/schemas/Collection" /projects/{projectID}/collections/{collectionID}: get: tags: [Collections] operationId: getCollection security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/CollectionID" responses: "200": description: Collection details content: application/json: schema: $ref: "#/components/schemas/Collection" patch: tags: [Collections] operationId: updateCollection security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/CollectionID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateCollectionRequest" responses: "200": description: Collection updated content: application/json: schema: $ref: "#/components/schemas/Collection" delete: tags: [Collections] operationId: deleteCollection security: - bearerAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/CollectionID" responses: "204": description: Collection deleted /collections/{collectionID}/documents: get: tags: [Collections] operationId: listDocuments security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/CollectionID" - $ref: "#/components/parameters/PaginationLimit" - $ref: "#/components/parameters/PaginationOffset" - name: filter in: query description: > PostgREST-style filters as comma-separated `field.op.value` terms. Ops: `eq`, `neq`, `gt`, `gte`, `lt`, `lte` (numeric values), `like` (ILIKE), `in` (pipe-separated values, e.g. `role.in.admin|editor`), `is` (`null`|`true`|`false`). Dotted paths index into the document data, e.g. `meta.city.eq.Prague`. Column fields `id`, `created_at`, `updated_at` are also allowed. schema: { type: string } example: "status.eq.active,age.gt.18" - name: order in: query description: > Comma-separated `field.asc|field.desc` list (default `created_at.desc`). Dotted paths index into document data. schema: { type: string } example: "name.asc,created_at.desc" responses: "200": description: List of documents in the collection content: application/json: schema: $ref: "#/components/schemas/DocumentListResponse" post: tags: [Collections] operationId: createDocument security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/CollectionID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateDocumentRequest" responses: "201": description: Document created content: application/json: schema: $ref: "#/components/schemas/Document" /collections/{collectionID}/documents/{documentID}: get: tags: [Collections] operationId: getDocument security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/CollectionID" - $ref: "#/components/parameters/DocumentID" responses: "200": description: Document details content: application/json: schema: $ref: "#/components/schemas/Document" patch: tags: [Collections] operationId: updateDocument security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/CollectionID" - $ref: "#/components/parameters/DocumentID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateDocumentRequest" responses: "200": description: Document updated content: application/json: schema: $ref: "#/components/schemas/Document" delete: tags: [Collections] operationId: deleteDocument security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/CollectionID" - $ref: "#/components/parameters/DocumentID" responses: "204": description: Document deleted /db/status: get: tags: [Databases] operationId: getDbxStatus security: - bearerAuth: [] - apiKeyAuth: [] responses: "200": description: DBX subprocess availability content: application/json: schema: $ref: "#/components/schemas/DBXStatus" /projects/{projectID}/db-connections: get: tags: [Databases] operationId: listDbConnections security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" responses: "200": description: Saved database connections for the project content: application/json: schema: $ref: "#/components/schemas/DBConnectionListResponse" "401": $ref: "#/components/responses/ErrorResponse" post: tags: [Databases] operationId: createDbConnection security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateDBConnectionRequest" responses: "201": description: Connection created content: application/json: schema: $ref: "#/components/schemas/DBConnection" "401": $ref: "#/components/responses/ErrorResponse" "409": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/db-connections/{connectionID}: delete: tags: [Databases] operationId: deleteDbConnection security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/ConnectionID" responses: "204": description: Connection deleted "404": $ref: "#/components/responses/ErrorResponse" "409": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/db-connections/{connectionID}/test: post: tags: [Databases] operationId: testDbConnection security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/ConnectionID" responses: "200": description: Connectivity probe result content: application/json: schema: $ref: "#/components/schemas/DBConnectionTestResult" "404": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/db-connections/{connectionID}/databases: get: tags: [Databases] operationId: listDbDatabases security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/ConnectionID" responses: "200": description: Databases reachable through the connection content: application/json: schema: $ref: "#/components/schemas/DBDatabaseListResponse" "404": $ref: "#/components/responses/ErrorResponse" "502": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/db-connections/{connectionID}/tables: get: tags: [Databases] operationId: listDbTables security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/ConnectionID" - $ref: "#/components/parameters/DatabaseQuery" - $ref: "#/components/parameters/SchemaQuery" responses: "200": description: Tables, views, or collections in the connection content: application/json: schema: $ref: "#/components/schemas/DBTableListResponse" "404": $ref: "#/components/responses/ErrorResponse" "502": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/db-connections/{connectionID}/describe: get: tags: [Databases] operationId: describeDbTable security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/ConnectionID" - $ref: "#/components/parameters/TableQuery" - $ref: "#/components/parameters/DatabaseQuery" - $ref: "#/components/parameters/SchemaQuery" responses: "200": description: Column definitions for a table content: application/json: schema: $ref: "#/components/schemas/DBDescribeResponse" "404": $ref: "#/components/responses/ErrorResponse" "502": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/db-connections/{connectionID}/schema: get: tags: [Databases] operationId: getDbSchemaContext security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/ConnectionID" - $ref: "#/components/parameters/DatabaseQuery" - $ref: "#/components/parameters/SchemaQuery" responses: "200": description: Compact schema context for the connection content: application/json: schema: $ref: "#/components/schemas/DBSchemaContextResponse" "404": $ref: "#/components/responses/ErrorResponse" "502": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/db-connections/{connectionID}/foreign-keys: get: tags: [Databases] operationId: listDbForeignKeys security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/ConnectionID" - $ref: "#/components/parameters/DatabaseQuery" - $ref: "#/components/parameters/SchemaQuery" responses: "200": description: Foreign key edges for the schema graph content: application/json: schema: $ref: "#/components/schemas/DBForeignKeysResponse" "404": $ref: "#/components/responses/ErrorResponse" "502": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/db-connections/{connectionID}/query: post: tags: [Databases] operationId: executeDbQuery security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/ConnectionID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/DBQueryRequest" responses: "200": description: Query result (at most 100 rows) content: application/json: schema: $ref: "#/components/schemas/DBQueryResponse" "404": $ref: "#/components/responses/ErrorResponse" "502": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/db-connections/{connectionID}/redis: post: tags: [Databases] operationId: executeDbRedisCommand security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/ConnectionID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/DBRedisRequest" responses: "200": description: Redis command output content: application/json: schema: $ref: "#/components/schemas/DBRedisResponse" "404": $ref: "#/components/responses/ErrorResponse" "502": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/db-connections/{connectionID}/transfer: post: tags: [Databases] operationId: transferDbRows summary: Move query results from this connection to another saved connection security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/ConnectionID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/DBTransferRequest" responses: "200": description: Transfer result content: application/json: schema: $ref: "#/components/schemas/DBTransferResponse" "404": $ref: "#/components/responses/ErrorResponse" "502": $ref: "#/components/responses/ErrorResponse" /ingest: post: tags: [Telemetry] operationId: ingestTelemetry summary: Ingest one telemetry event or a batch of up to 500 security: - apiKeyAuth: [] requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/IngestRequest" responses: "202": description: Accepted event count content: application/json: schema: $ref: "#/components/schemas/IngestResponse" "400": $ref: "#/components/responses/ErrorResponse" "401": $ref: "#/components/responses/ErrorResponse" "413": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/events: get: tags: [Telemetry] operationId: listTelemetryEvents security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - name: type in: query schema: type: string enum: [error, metric, log, heartbeat, event] - name: component in: query schema: type: string - name: fingerprint in: query schema: type: string - name: before in: query schema: type: integer - name: limit in: query schema: type: integer responses: "200": description: Telemetry events, newest first content: application/json: schema: $ref: "#/components/schemas/TelemetryEventListResponse" "401": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/issues: get: tags: [Telemetry] operationId: listTelemetryIssues security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - name: days in: query schema: type: integer responses: "200": description: Error groups by fingerprint content: application/json: schema: $ref: "#/components/schemas/TelemetryIssueListResponse" "401": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/components: get: tags: [Telemetry] operationId: listTelemetryComponents security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" responses: "200": description: Registered telemetry components content: application/json: schema: $ref: "#/components/schemas/TelemetryComponentListResponse" "401": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/components/{componentID}: delete: tags: [Telemetry] operationId: deleteTelemetryComponent security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/ComponentID" responses: "204": description: Component deleted "401": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/telemetry/stats: get: tags: [Telemetry] operationId: getTelemetryStats security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - name: window in: query schema: type: string enum: [1h, 24h, 7d, 30d] responses: "200": description: Aggregated telemetry statistics content: application/json: schema: $ref: "#/components/schemas/TelemetryStats" "401": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/telemetry/metrics/series: get: tags: [Telemetry] operationId: getTelemetryMetricSeries security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - name: name in: query required: true schema: type: string - name: window in: query schema: type: string enum: [1h, 24h, 7d, 30d] responses: "200": description: Aggregated metric series content: application/json: schema: $ref: "#/components/schemas/MetricSeriesResponse" "401": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/telemetry/stream: get: tags: [Telemetry] operationId: streamTelemetryEvents summary: Server-sent events stream of live telemetry. EventSource clients pass `?api_key=` or `?token=` since headers cannot be set. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - name: api_key in: query schema: type: string - name: token in: query schema: type: string responses: "200": description: text/event-stream of TelemetryEvent messages "401": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/integrations: get: tags: [Integrations] operationId: listIntegrations security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" responses: "200": description: External service integrations for the project content: application/json: schema: $ref: "#/components/schemas/IntegrationListResponse" "401": $ref: "#/components/responses/ErrorResponse" post: tags: [Integrations] operationId: createIntegration security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateIntegrationRequest" responses: "201": description: Integration created content: application/json: schema: $ref: "#/components/schemas/Integration" "401": $ref: "#/components/responses/ErrorResponse" "409": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/integrations/{integrationID}: delete: tags: [Integrations] operationId: deleteIntegration security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/IntegrationID" responses: "204": description: Integration deleted "404": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/integrations/{integrationID}/test: post: tags: [Integrations] operationId: testIntegration summary: Health check — HTTP GET against the integration's base URL with stored credentials (~5s timeout). Updates status and last_health_at. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/IntegrationID" responses: "200": description: Connectivity probe result content: application/json: schema: $ref: "#/components/schemas/IntegrationTestResult" "404": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/integrations/{integrationID}/analytics: get: tags: [Integrations] operationId: getIntegrationAnalytics summary: Read-only analytics pulled from the integration (Rybbit). The API key never leaves the server. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/IntegrationID" - name: site in: query required: false schema: type: string description: Rybbit site id override; defaults to the integration's configured site, else first accessible site. - name: days in: query required: false schema: type: integer minimum: 1 maximum: 365 default: 30 responses: "200": description: Normalized analytics payload content: application/json: schema: $ref: "#/components/schemas/IntegrationAnalytics" "400": $ref: "#/components/responses/ErrorResponse" "404": $ref: "#/components/responses/ErrorResponse" "502": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/webhooks: get: tags: [Webhooks] operationId: listWebhooks security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" responses: "200": description: Outbound webhooks for the project content: application/json: schema: $ref: "#/components/schemas/WebhookListResponse" "401": $ref: "#/components/responses/ErrorResponse" post: tags: [Webhooks] operationId: createWebhook summary: Deliveries are POSTed as JSON with X-Primora-Event and X-Primora-Signature (sha256=). If no secret is supplied one is generated and returned once. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateWebhookRequest" responses: "201": description: Webhook created content: application/json: schema: $ref: "#/components/schemas/WebhookCreateResponse" "401": $ref: "#/components/responses/ErrorResponse" "409": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/webhooks/{webhookID}: patch: tags: [Webhooks] operationId: updateWebhook security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/WebhookID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateWebhookRequest" responses: "200": description: Webhook updated content: application/json: schema: $ref: "#/components/schemas/WebhookCreateResponse" "404": $ref: "#/components/responses/ErrorResponse" delete: tags: [Webhooks] operationId: deleteWebhook security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/WebhookID" responses: "204": description: Webhook deleted "404": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/webhooks/{webhookID}/deliveries: get: tags: [Webhooks] operationId: listWebhookDeliveries security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/WebhookID" - name: limit in: query required: false schema: type: integer minimum: 1 maximum: 200 default: 50 responses: "200": description: Recent delivery attempts, newest first content: application/json: schema: $ref: "#/components/schemas/WebhookDeliveryListResponse" "404": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/webhooks/{webhookID}/test: post: tags: [Webhooks] operationId: testWebhook summary: Queues a webhook.test ping delivery. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/WebhookID" responses: "202": description: Test delivery queued content: application/json: schema: $ref: "#/components/schemas/WebhookDelivery" "404": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/deploy-markers: post: tags: [Webhooks] operationId: createDeployMarker summary: Records a deployment marker in the audit log and fires the deploy.marker webhook event. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateDeployMarkerRequest" responses: "201": description: Marker recorded content: application/json: schema: $ref: "#/components/schemas/DeployMarker" "401": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/jobs: get: tags: [Automation] operationId: listScheduledJobs security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" responses: "200": description: Scheduled jobs for the project content: application/json: schema: $ref: "#/components/schemas/ScheduledJobListResponse" "401": $ref: "#/components/responses/ErrorResponse" post: tags: [Automation] operationId: createScheduledJob summary: Runs a signed POST to the job URL on a cron schedule (standard 5-field cron or @every/@hourly descriptors). Payload is signed X-Primora-Signature like webhook deliveries; an empty secret generates one returned once. HTTPS required for public targets, HTTP allowed for private/self-hosted. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateScheduledJobRequest" responses: "201": description: Job created content: application/json: schema: $ref: "#/components/schemas/ScheduledJobCreateResponse" "400": $ref: "#/components/responses/ErrorResponse" "401": $ref: "#/components/responses/ErrorResponse" "409": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/jobs/{jobID}: patch: tags: [Automation] operationId: updateScheduledJob security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/JobID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateScheduledJobRequest" responses: "200": description: Job updated content: application/json: schema: $ref: "#/components/schemas/ScheduledJobCreateResponse" "404": $ref: "#/components/responses/ErrorResponse" delete: tags: [Automation] operationId: deleteScheduledJob security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/JobID" responses: "204": description: Job deleted "404": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/jobs/{jobID}/runs: get: tags: [Automation] operationId: listScheduledJobRuns summary: Recent executions, newest first. History is capped at 200 runs per job. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/JobID" - name: limit in: query required: false schema: type: integer minimum: 1 maximum: 200 default: 50 responses: "200": description: Job run history content: application/json: schema: $ref: "#/components/schemas/ScheduledJobRunListResponse" "404": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/jobs/{jobID}/run: post: tags: [Automation] operationId: runScheduledJob summary: Triggers an out-of-schedule manual run. Returns the run row in "running" state. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/JobID" responses: "202": description: Manual run queued content: application/json: schema: $ref: "#/components/schemas/ScheduledJobRun" "404": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/realtime/stream: get: tags: [Automation] operationId: streamRealtimeEvents summary: Server-sent events stream of project domain events — document/object mutations, new issues, deploy markers, job runs. EventSource clients pass `?api_key=` or `?token=` since headers cannot be set. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - name: api_key in: query schema: type: string - name: token in: query schema: type: string responses: "200": description: text/event-stream of {type, occurred_at, data} messages "401": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/secrets: get: tags: [Secrets] operationId: listProjectSecrets summary: Lists project secrets — metadata only, values are never included. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" responses: "200": description: Project secrets (metadata only) content: application/json: schema: $ref: "#/components/schemas/ProjectSecretListResponse" "401": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/secrets/{name}: put: tags: [Secrets] operationId: setProjectSecret summary: Creates or replaces a project secret (AES-256-GCM at rest). Omitted url/notes keep existing values. Job payloads can reference it as secret://NAME, resolved at delivery. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/SecretName" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/SetProjectSecretRequest" responses: "200": description: Secret stored content: application/json: schema: $ref: "#/components/schemas/ProjectSecret" "400": $ref: "#/components/responses/ErrorResponse" "401": $ref: "#/components/responses/ErrorResponse" delete: tags: [Secrets] operationId: deleteProjectSecret security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/SecretName" responses: "204": description: Secret deleted "401": $ref: "#/components/responses/ErrorResponse" "404": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/secrets/{name}/reveal: post: tags: [Secrets] operationId: revealProjectSecret summary: Returns the decrypted value. Every call is audit-logged. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - $ref: "#/components/parameters/SecretName" responses: "200": description: Decrypted value content: application/json: schema: $ref: "#/components/schemas/RevealProjectSecretResponse" "401": $ref: "#/components/responses/ErrorResponse" "404": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/alerts: get: tags: [Alerts] operationId: listAlertRules summary: Lists telemetry alert rules for the project. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" responses: "200": description: Alert rules content: application/json: schema: $ref: "#/components/schemas/AlertRuleListResponse" "401": $ref: "#/components/responses/ErrorResponse" post: tags: [Alerts] operationId: createAlertRule summary: Creates an alert rule. Rules fire `alert.fired`/`alert.resolved` domain events on state transitions — subscribe a webhook to those event types to receive them. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpsertAlertRuleRequest" responses: "201": description: Alert rule created content: application/json: schema: $ref: "#/components/schemas/AlertRule" "400": $ref: "#/components/responses/ErrorResponse" "401": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/alerts/{ruleID}: put: tags: [Alerts] operationId: updateAlertRule security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - name: ruleID in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpsertAlertRuleRequest" responses: "200": description: Alert rule updated content: application/json: schema: $ref: "#/components/schemas/AlertRule" "400": $ref: "#/components/responses/ErrorResponse" "401": $ref: "#/components/responses/ErrorResponse" "404": $ref: "#/components/responses/ErrorResponse" delete: tags: [Alerts] operationId: deleteAlertRule security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - name: ruleID in: path required: true schema: type: string format: uuid responses: "204": description: Alert rule deleted "401": $ref: "#/components/responses/ErrorResponse" "404": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/inbound-hooks: get: tags: [Inbound] operationId: listInboundHooks summary: Lists inbound webhook endpoints for the project. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" responses: "200": description: Inbound hooks content: application/json: schema: $ref: "#/components/schemas/InboundHookListResponse" "401": $ref: "#/components/responses/ErrorResponse" post: tags: [Inbound] operationId: createInboundHook summary: Creates an inbound hook. `event` mode republishes bodies as `inbound.received` domain events; `job` mode triggers a scheduled-job run with the body as its payload. Set `secret` to require `X-Primora-Signature` (HMAC-SHA256 over the raw body). security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateInboundHookRequest" responses: "201": description: Inbound hook created content: application/json: schema: $ref: "#/components/schemas/InboundHook" "400": $ref: "#/components/responses/ErrorResponse" "401": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/inbound-hooks/{hookID}: delete: tags: [Inbound] operationId: deleteInboundHook security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - name: hookID in: path required: true schema: type: string format: uuid responses: "204": description: Inbound hook deleted "401": $ref: "#/components/responses/ErrorResponse" "404": $ref: "#/components/responses/ErrorResponse" /hooks/{token}: post: tags: [Inbound] operationId: receiveInboundHook summary: Public ingest endpoint — the URL token is the credential. Body must be JSON (max 256 KiB); sign with X-Primora-Signature when the hook has a secret. security: [] parameters: - name: token in: path required: true schema: type: string - name: X-Primora-Signature in: header schema: type: string requestBody: content: application/json: schema: type: object responses: "202": description: Accepted content: application/json: schema: $ref: "#/components/schemas/InboundReceiveResponse" "400": $ref: "#/components/responses/ErrorResponse" "401": $ref: "#/components/responses/ErrorResponse" "404": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/emails: get: tags: [Email] operationId: listEmailLog summary: Recent transactional emails sent for this project (invitations etc.) — every send is logged with its outcome. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - name: limit in: query schema: type: integer default: 50 maximum: 200 responses: "200": description: Email log entries content: application/json: schema: $ref: "#/components/schemas/EmailLogListResponse" "401": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/functions: get: tags: [Functions] operationId: listFunctions summary: List functions in a project. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" responses: "200": description: Functions content: application/json: schema: $ref: "#/components/schemas/FunctionListResponse" "401": $ref: "#/components/responses/ErrorResponse" post: tags: [Functions] operationId: createFunction summary: Create a function (name, JS/TS code, runtime bun|deno). security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateFunctionRequest" responses: "201": description: Created function content: application/json: schema: $ref: "#/components/schemas/Function" "400": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/functions/{functionID}: get: tags: [Functions] operationId: getFunction summary: Function metadata plus its source code. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - name: functionID in: path required: true schema: { type: string, format: uuid } responses: "200": description: Function with code content: application/json: schema: $ref: "#/components/schemas/FunctionDetailResponse" "404": $ref: "#/components/responses/ErrorResponse" put: tags: [Functions] operationId: updateFunction summary: Update function code and/or enabled state. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - name: functionID in: path required: true schema: { type: string, format: uuid } requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateFunctionRequest" responses: "200": description: Updated function content: application/json: schema: $ref: "#/components/schemas/Function" "404": $ref: "#/components/responses/ErrorResponse" delete: tags: [Functions] operationId: deleteFunction summary: Delete a function and its run history. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - name: functionID in: path required: true schema: { type: string, format: uuid } responses: "204": description: Deleted "404": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/functions/{functionID}/invoke: post: tags: [Functions] operationId: invokeFunction summary: Execute the function with a JSON payload on stdin; returns the recorded run. 502 when the run fails or times out. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - name: functionID in: path required: true schema: { type: string, format: uuid } requestBody: required: false content: application/json: schema: $ref: "#/components/schemas/InvokeFunctionRequest" responses: "200": description: Recorded run (status success) content: application/json: schema: $ref: "#/components/schemas/FunctionRun" "502": description: Recorded run (status error|timeout) content: application/json: schema: $ref: "#/components/schemas/FunctionRun" "404": $ref: "#/components/responses/ErrorResponse" /projects/{projectID}/functions/{functionID}/runs: get: tags: [Functions] operationId: listFunctionRuns summary: Recent runs for a function. security: - bearerAuth: [] - apiKeyAuth: [] parameters: - $ref: "#/components/parameters/ProjectID" - name: functionID in: path required: true schema: { type: string, format: uuid } - name: limit in: query schema: type: integer default: 50 maximum: 200 responses: "200": description: Function runs content: application/json: schema: $ref: "#/components/schemas/FunctionRunListResponse" "404": $ref: "#/components/responses/ErrorResponse" components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT apiKeyAuth: type: apiKey in: header name: X-API-Key parameters: OrganizationID: name: organizationID in: path required: true schema: type: string format: uuid ProjectID: name: projectID in: path required: true schema: type: string format: uuid SecretName: name: name in: path required: true schema: type: string pattern: "^[A-Za-z_][A-Za-z0-9_]*$" BucketID: name: bucketID in: path required: true schema: type: string format: uuid ApiKeyID: name: apiKeyID in: path required: true schema: type: string format: uuid CollectionID: name: collectionID in: path required: true schema: type: string format: uuid DocumentID: name: documentID in: path required: true schema: type: string format: uuid InvitationID: name: invitationID in: path required: true schema: type: string format: uuid ComponentID: name: componentID in: path required: true schema: type: string format: uuid UserID: name: userID in: path required: true schema: type: string format: uuid PaginationLimit: name: limit in: query required: false schema: type: integer format: int32 minimum: 1 maximum: 200 default: 50 PaginationOffset: name: offset in: query required: false schema: type: integer format: int32 minimum: 0 maximum: 100000 default: 0 AuditQuery: name: q in: query required: false schema: type: string AuditAction: name: action in: query required: false schema: type: string ObjectKey: name: objectKey in: path required: true schema: type: string ConnectionID: name: connectionID in: path required: true schema: type: string format: uuid IntegrationID: name: integrationID in: path required: true schema: type: string format: uuid WebhookID: name: webhookID in: path required: true schema: type: string format: uuid JobID: name: jobID in: path required: true schema: type: string format: uuid DatabaseQuery: name: database in: query required: false schema: type: string SchemaQuery: name: schema in: query required: false schema: type: string TableQuery: name: table in: query required: true schema: type: string responses: ErrorResponse: description: Error response content: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" schemas: HealthStatus: type: object properties: status: type: string required: [status] ReadinessStatus: type: object properties: status: type: string checks: type: object additionalProperties: type: string required: [status, checks] ErrorEnvelope: type: object properties: status: type: string error: type: object properties: code: type: string message: type: string required: [code, message] required: [status, error] InstancePublicConfig: type: object properties: signup_enabled: type: boolean description: Whether public sign-up is open (bootstrap always allowed) bootstrap_required: type: boolean description: True when no user exists yet — first sign-up becomes instance admin social_providers: type: array items: type: string description: Enabled OAuth providers — empty on self-hosted required: [signup_enabled, bootstrap_required, social_providers] InstanceSetting: type: object properties: key: type: string type: type: string enum: [bool, int, string] secret: type: boolean is_set: type: boolean description: True when an in-app override exists source: type: string enum: [app, env, default] value: description: Effective value — never returned for secret keys required: [key, type, secret, is_set, source] InstanceSettingsResponse: type: object properties: settings: type: array items: $ref: "#/components/schemas/InstanceSetting" required: [settings] UpdateInstanceSettingRequest: type: object properties: value: description: JSON value matching the key's declared type required: [value] BootstrapRequest: type: object properties: organizationName: type: string organizationSlug: type: string projectName: type: string projectSlug: type: string description: type: string nullable: true required: [organizationName, organizationSlug, projectName, projectSlug] BootstrapResponse: type: object properties: organization_id: type: string format: uuid organization_slug: type: string organization_name: type: string project_id: type: string format: uuid project_slug: type: string project_name: type: string required: - organization_id - organization_slug - organization_name - project_id - project_slug - project_name UserSummary: type: object properties: id: type: string format: uuid authSubject: type: string email: type: string name: type: string emailVerified: type: boolean required: [id, authSubject, email, name, emailVerified] ProjectSummary: type: object properties: id: type: string format: uuid name: type: string slug: type: string description: type: string nullable: true membershipRole: type: string nullable: true required: [id, name, slug] OrganizationSummary: type: object properties: id: type: string format: uuid name: type: string slug: type: string membershipRole: type: string projects: type: array items: $ref: "#/components/schemas/ProjectSummary" required: [id, name, slug, membershipRole, projects] MeResponse: type: object properties: user: $ref: "#/components/schemas/UserSummary" organizations: type: array items: $ref: "#/components/schemas/OrganizationSummary" required: [user, organizations] OrganizationMembership: type: object properties: id: type: string format: uuid slug: type: string name: type: string membership_role: type: string required: [id, slug, name, membership_role] CreateOrganizationRequest: type: object properties: name: type: string slug: type: string required: [name, slug] UpdateOrganizationRequest: type: object properties: name: type: string slug: type: string required: [name, slug] OrganizationMember: type: object properties: user_id: type: string format: uuid email: type: string name: type: string email_verified: type: boolean role: type: string enum: [owner, admin, member] joined_at: type: string format: date-time required: [user_id, email, name, email_verified, role, joined_at] UpdateOrganizationMemberRoleRequest: type: object properties: role: type: string enum: [owner, admin, member] required: [role] CreateProjectRequest: type: object properties: name: type: string slug: type: string description: type: string nullable: true required: [name, slug] UpdateProjectRequest: type: object properties: name: type: string slug: type: string description: type: string nullable: true retention_events_days: type: integer minimum: 0 maximum: 3650 description: Telemetry event retention; 0 disables the sweep. retention_audit_days: type: integer minimum: 0 maximum: 3650 description: Audit log retention; 0 disables the sweep. retention_webhook_days: type: integer minimum: 0 maximum: 3650 description: Webhook delivery retention; 0 disables the sweep. required: [name, slug] Project: type: object properties: id: type: string format: uuid organization_id: type: string format: uuid slug: type: string name: type: string description: type: string nullable: true membership_role: type: string nullable: true retention_events_days: type: integer retention_audit_days: type: integer retention_webhook_days: type: integer required: [id, organization_id, slug, name] ProjectOverview: type: object properties: project_id: type: string format: uuid organization_id: type: string format: uuid project_slug: type: string project_name: type: string member_count: type: integer format: int64 active_api_key_count: type: integer format: int64 bucket_count: type: integer format: int64 object_count: type: integer format: int64 object_bytes_total: type: integer format: int64 pending_invitation_count: type: integer format: int64 audit_events_24h: type: integer format: int64 last_audit_at: type: string format: date-time nullable: true integration_count: type: integer format: int64 description: Connectors attached to the project. webhook_count: type: integer format: int64 description: Enabled outbound webhooks. required: - project_id - organization_id - project_slug - project_name - member_count - active_api_key_count - bucket_count - object_count - object_bytes_total - pending_invitation_count - audit_events_24h - integration_count - webhook_count ProjectMember: type: object properties: user_id: type: string format: uuid email: type: string name: type: string email_verified: type: boolean role: type: string enum: [admin, developer, viewer] joined_at: type: string format: date-time required: [user_id, email, name, email_verified, role, joined_at] UpdateProjectMemberRoleRequest: type: object properties: role: type: string enum: [admin, developer, viewer] required: [role] CreateInvitationRequest: type: object properties: email: type: string format: email orgRole: type: string enum: [owner, admin, member] projectId: type: string format: uuid nullable: true projectRole: type: string enum: [admin, developer, viewer] nullable: true redirectUrl: type: string format: uri nullable: true required: [email, orgRole] Invitation: type: object properties: id: type: string format: uuid email: type: string format: email expiresAt: type: string format: date-time required: [id, email, expiresAt] OrganizationInvitation: type: object properties: id: type: string format: uuid organization_id: type: string format: uuid project_id: type: string format: uuid nullable: true project_name: type: string nullable: true email: type: string format: email org_role: type: string enum: [owner, admin, member] project_role: type: string enum: [admin, developer, viewer] nullable: true expires_at: type: string format: date-time accepted_at: type: string format: date-time nullable: true invited_by_user_id: type: string format: uuid nullable: true created_at: type: string format: date-time status: type: string enum: [pending, accepted, expired] required: - id - organization_id - email - org_role - expires_at - created_at - status AcceptInvitationRequest: type: object properties: token: type: string required: [token] CreateApiKeyRequest: type: object properties: name: type: string scopes: type: array items: type: string enum: [ingest, read, write, admin] description: >- Access scopes for the key. admin unlocks everything; write unlocks read + ingest + non-admin mutations; read and ingest unlock only themselves. Omitted means ["admin"] (full access, legacy behaviour). required: [name] ApiKey: type: object properties: id: type: string format: uuid project_id: type: string format: uuid name: type: string prefix: type: string scopes: type: array items: type: string enum: [ingest, read, write, admin] last_used_at: type: string format: date-time nullable: true revoked_at: type: string format: date-time nullable: true required: [id, project_id, name, prefix] CreatedApiKey: type: object properties: id: type: string format: uuid prefix: type: string secret: type: string name: type: string scopes: type: array items: type: string enum: [ingest, read, write, admin] required: [id, prefix, secret, name] CreateBucketRequest: type: object properties: name: type: string slug: type: string visibility: type: string enum: [private, public] required: [name, slug, visibility] UpdateBucketRequest: type: object properties: name: type: string slug: type: string visibility: type: string enum: [private, public] required: [name, slug, visibility] Bucket: type: object properties: id: type: string format: uuid project_id: type: string format: uuid slug: type: string name: type: string visibility: type: string required: [id, project_id, slug, name, visibility] BucketObject: type: object properties: id: type: string format: uuid bucket_id: type: string format: uuid object_key: type: string content_type: type: string size_bytes: type: integer format: int64 checksum_sha256: type: string created_at: type: string format: date-time required: [id, bucket_id, object_key, content_type, size_bytes, checksum_sha256, created_at] UpdateBucketObjectRequest: type: object properties: newObjectKey: type: string destinationBucketId: type: string format: uuid nullable: true required: [newObjectKey] CopyBucketObjectRequest: type: object properties: objectKey: type: string newObjectKey: type: string destinationBucketId: type: string format: uuid nullable: true required: [objectKey, newObjectKey] PresignObjectRequest: type: object properties: key: type: string description: Object key inside the bucket. op: type: string enum: [download, upload] default: download description: download → GET URL (object must exist); upload → PUT URL (creates/overwrites). ttl_seconds: type: integer minimum: 60 maximum: 3600 default: 900 required: [key] PresignedURL: type: object properties: url: type: string description: Fully-signed URL — call it with the returned method, no auth header needed. method: type: string enum: [GET, PUT] expires_at: type: string format: date-time required: [url, method, expires_at] PaginationMetadata: type: object properties: total: type: integer format: int64 limit: type: integer format: int32 offset: type: integer format: int32 has_more: type: boolean required: [total, limit, offset, has_more] BucketObjectListResponse: allOf: - $ref: "#/components/schemas/PaginationMetadata" - type: object properties: items: type: array items: $ref: "#/components/schemas/BucketObject" required: [items] AuditLog: type: object properties: id: type: string format: uuid created_at: type: string format: date-time action: type: string resource_type: type: string resource_id: type: string request_id: type: string metadata: type: object additionalProperties: true required: [id, created_at, action, resource_type, resource_id, request_id, metadata] AuditLogListResponse: allOf: - $ref: "#/components/schemas/PaginationMetadata" - type: object properties: items: type: array items: $ref: "#/components/schemas/AuditLog" required: [items] Collection: type: object properties: id: type: string format: uuid project_id: type: string format: uuid slug: type: string name: type: string description: type: string nullable: true schema: type: object description: JSON Schema for the collection created_at: type: string format: date-time updated_at: type: string format: date-time required: [id, project_id, slug, name, schema, created_at, updated_at] CreateCollectionRequest: type: object properties: slug: type: string name: type: string description: type: string nullable: true schema: type: object required: [slug, name] UpdateCollectionRequest: type: object properties: name: type: string description: type: string nullable: true schema: type: object Document: type: object properties: id: type: string format: uuid collection_id: type: string format: uuid data: type: object created_at: type: string format: date-time updated_at: type: string format: date-time required: [id, collection_id, data, created_at, updated_at] CreateDocumentRequest: type: object properties: data: type: object required: [data] UpdateDocumentRequest: type: object properties: data: type: object required: [data] CollectionListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/Collection" required: [items] DocumentListResponse: allOf: - $ref: "#/components/schemas/PaginationMetadata" - type: object properties: items: type: array items: $ref: "#/components/schemas/Document" required: [items] DBXStatus: type: object properties: available: type: boolean command: type: string reason: type: string install: type: string required: [available] DBConnection: type: object properties: id: type: string format: uuid project_id: type: string format: uuid name: type: string db_type: type: string host: type: string port: type: integer nullable: true database: type: string username: type: string ssl: type: boolean nullable: true is_managed: type: boolean has_password: type: boolean required: [id, project_id, name, db_type, host, is_managed, has_password] CreateDBConnectionRequest: type: object properties: name: type: string db_type: type: string enum: [postgres, mysql, sqlite, redis, mongodb, sqlserver, duckdb, clickhouse, cassandra] host: type: string port: type: integer database: type: string username: type: string password: type: string ssl: type: boolean driver_profile: type: string required: [name, db_type, host] DBConnectionListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/DBConnection" required: [items] DBConnectionTestResult: type: object properties: ok: type: boolean error: type: string required: [ok] DBObject: type: object properties: name: type: string kind: type: string required: [name, kind] DBMDTable: type: object properties: columns: type: array items: type: string rows: type: array items: type: array items: type: string note: type: string required: [columns, rows] DBDatabaseListResponse: type: object properties: databases: type: array items: type: string raw: type: string required: [databases] DBTableListResponse: type: object properties: tables: type: array items: $ref: "#/components/schemas/DBObject" table: $ref: "#/components/schemas/DBMDTable" raw: type: string DBDescribeResponse: type: object properties: table: $ref: "#/components/schemas/DBMDTable" raw: type: string DBSchemaContextResponse: type: object properties: context: type: string required: [context] DBForeignKeyEdge: type: object properties: schema: type: string table: type: string column: type: string ref_schema: type: string ref_table: type: string ref_column: type: string required: [table, column, ref_table, ref_column] DBForeignKeysResponse: type: object properties: edges: type: array items: $ref: "#/components/schemas/DBForeignKeyEdge" raw: type: string note: type: string required: [edges] DBQueryRequest: type: object properties: sql: type: string database: type: string required: [sql] DBQueryResponse: type: object properties: table: $ref: "#/components/schemas/DBMDTable" raw: type: string DBRedisRequest: type: object properties: command: type: string db: type: integer required: [command] DBRedisResponse: type: object properties: output: type: string required: [output] DBTransferRequest: type: object properties: target_connection_id: type: string format: uuid query: type: string description: SELECT run against the source connection. database: type: string target_table: type: string description: Destination table for SQL targets (schema-qualified allowed). key_pattern: type: string description: >- Redis targets: SET key template with {column} placeholders, e.g. "user:{id}". value_column: type: string description: >- Redis targets: column whose cell becomes the value. Omitted stores the whole row as JSON. ttl_seconds: type: integer minimum: 0 description: Optional EX TTL applied to each Redis SET. limit: type: integer minimum: 1 maximum: 1000 default: 1000 required: [target_connection_id, query] DBTransferResponse: type: object properties: transferred: type: integer truncated: type: boolean source: type: string target: type: string required: [transferred, truncated, source, target] IngestEvent: type: object properties: type: type: string enum: [error, metric, log, heartbeat, event] component: type: string description: Auto-registered under the project on first use kind: type: string enum: [frontend, backend, database, android, desktop, web, other] severity: type: string message: type: string payload: type: object additionalProperties: true fingerprint: type: string description: Error grouping key; derived from message when omitted ts: type: string format: date-time required: [type] IngestRequest: oneOf: - $ref: "#/components/schemas/IngestEvent" - type: object properties: events: type: array items: $ref: "#/components/schemas/IngestEvent" maxItems: 500 required: [events] IngestResponse: type: object properties: accepted: type: integer required: [accepted] TelemetryEvent: type: object properties: id: type: integer project_id: type: string format: uuid component_id: type: string format: uuid nullable: true component_name: type: string type: type: string severity: type: string message: type: string payload: type: object additionalProperties: true fingerprint: type: string ts: type: string format: date-time required: [id, project_id, type, severity, message, ts] TelemetryEventListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/TelemetryEvent" required: [items] TelemetryIssue: type: object properties: fingerprint: type: string message: type: string component_id: type: string format: uuid nullable: true component_name: type: string count: type: integer first_seen: type: string format: date-time last_seen: type: string format: date-time severity: type: string required: [fingerprint, message, count, first_seen, last_seen, severity] TelemetryIssueListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/TelemetryIssue" required: [items] TelemetryComponent: type: object properties: id: type: string format: uuid name: type: string kind: type: string last_seen_at: type: string format: date-time nullable: true last_status: type: string errors_24h: type: integer events_24h: type: integer meta: type: object additionalProperties: true required: [id, name, kind, errors_24h, events_24h] TelemetryComponentListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/TelemetryComponent" required: [items] ComponentHealth: type: object properties: id: type: string format: uuid name: type: string kind: type: string last_seen_at: type: string format: date-time nullable: true last_status: type: string errors_24h: type: integer events_24h: type: integer meta: type: object additionalProperties: true required: [id, name, kind, errors_24h, events_24h] EventSeriesBucket: type: object properties: ts: type: string format: date-time counts: type: object additionalProperties: type: integer required: [ts, counts] TelemetryStats: type: object properties: window: type: integer bucket_sec: type: integer series: type: array items: $ref: "#/components/schemas/EventSeriesBucket" components: type: array items: $ref: "#/components/schemas/ComponentHealth" errors: type: integer events: type: integer metrics: type: integer metric_names: type: array items: type: string required: [window, bucket_sec, series, components, errors, events, metrics, metric_names] MetricPoint: type: object properties: ts: type: string format: date-time avg: type: number nullable: true p50: type: number nullable: true p95: type: number nullable: true max: type: number nullable: true count: type: integer required: [ts, count] MetricSeriesResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/MetricPoint" required: [items] Integration: type: object properties: id: type: string format: uuid project_id: type: string format: uuid type: type: string enum: [rybbit] name: type: string base_url: type: string site_id: type: string description: Connector-specific target (Rybbit site id). Optional. status: type: string enum: [unknown, ok, error] last_health_at: type: string format: date-time nullable: true has_credentials: type: boolean created_at: type: string format: date-time required: [id, project_id, type, name, base_url, status, has_credentials] CreateIntegrationRequest: type: object properties: name: type: string type: type: string enum: [rybbit] base_url: type: string description: Root URL of the self-hosted instance, e.g. https://analytics.example.com api_key: type: string description: Credential stored encrypted at rest; never returned by any endpoint. site_id: type: string required: [name, type, base_url] IntegrationListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/Integration" required: [items] IntegrationTestResult: type: object properties: ok: type: boolean status_code: type: integer error: type: string required: [ok] RybbitSite: type: object properties: site_id: type: string name: type: string domain: type: string required: [site_id, name] IntegrationAnalytics: type: object properties: integration_id: type: string format: uuid site: $ref: "#/components/schemas/RybbitSite" sites: type: array items: $ref: "#/components/schemas/RybbitSite" overview: type: object properties: sessions: type: number pageviews: type: number users: type: number pages_per_session: type: number bounce_rate: type: number session_duration: type: number series: type: array items: type: object properties: time: type: string sessions: type: number pageviews: type: number users: type: number bounce_rate: type: number top_pages: type: array items: $ref: "#/components/schemas/RybbitMetricItem" top_referrers: type: array items: $ref: "#/components/schemas/RybbitMetricItem" window_days: type: integer required: [integration_id, window_days] RybbitMetricItem: type: object properties: value: type: string count: type: number percentage: type: number required: [value, count] Webhook: type: object properties: id: type: string format: uuid project_id: type: string format: uuid url: type: string events: type: array items: type: string description: Event filter (issue.created, deploy.marker, webhook.test, job.run, alert.fired, alert.resolved, inbound.received, document.*, object.*). Empty means all events. enabled: type: boolean has_secret: type: boolean created_at: type: string format: date-time required: [id, project_id, url, events, enabled, has_secret] CreateWebhookRequest: type: object properties: url: type: string format: uri secret: type: string description: HMAC key for X-Primora-Signature. Generated when omitted. events: type: array items: type: string enum: [issue.created, deploy.marker, webhook.test, job.run, alert.fired, alert.resolved, inbound.received, document.created, document.updated, document.deleted, object.created, object.updated, object.deleted] enabled: type: boolean required: [url] UpdateWebhookRequest: type: object properties: url: type: string format: uri secret: type: string description: Replace the signing secret. Omit to keep the current one. events: type: array items: type: string enum: [issue.created, deploy.marker, webhook.test, job.run, alert.fired, alert.resolved, inbound.received, document.created, document.updated, document.deleted, object.created, object.updated, object.deleted] enabled: type: boolean WebhookCreateResponse: allOf: - $ref: "#/components/schemas/Webhook" - type: object properties: secret: type: string description: Plaintext secret — present only when Primora generated it. Store it; it cannot be recovered. WebhookListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/Webhook" required: [items] WebhookDelivery: type: object properties: id: type: string format: uuid webhook_id: type: string format: uuid event_type: type: string payload: type: object status: type: string enum: [pending, delivered, failed] attempts: type: integer last_status_code: type: integer nullable: true last_error: type: string delivered_at: type: string format: date-time nullable: true next_retry_at: type: string format: date-time nullable: true created_at: type: string format: date-time required: [id, webhook_id, event_type, status, attempts] WebhookDeliveryListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/WebhookDelivery" required: [items] CreateDeployMarkerRequest: type: object properties: version: type: string ref: type: string description: Git SHA, tag or branch being deployed. environment: type: string note: type: string DeployMarker: type: object properties: version: type: string ref: type: string environment: type: string note: type: string recorded_at: type: string format: date-time required: [recorded_at] ScheduledJob: type: object properties: id: type: string format: uuid project_id: type: string format: uuid name: type: string schedule: type: string description: Cron expression (5-field) or descriptor (@hourly, @every 30m). url: type: string function_id: type: string format: uuid nullable: true description: When set, the schedule invokes this function instead of POSTing url. payload: type: object description: Static JSON payload merged into every delivery's data field. enabled: type: boolean has_secret: type: boolean last_run_at: type: string format: date-time nullable: true last_status: type: string enum: [success, failed] nullable: true next_run_at: type: string format: date-time nullable: true created_at: type: string format: date-time required: [id, project_id, name, schedule, url, payload, enabled, has_secret] CreateScheduledJobRequest: type: object properties: name: type: string minLength: 2 schedule: type: string description: 'Cron expression, e.g. "*/15 * * * *", or descriptor like "@every 1h".' url: type: string format: uri description: Delivery target. Required unless function_id is set. function_id: type: string format: uuid description: Invoke this function on schedule instead of POSTing url. secret: type: string description: HMAC key for X-Primora-Signature. Generated when omitted. payload: type: object description: Static JSON payload delivered as data on every run. enabled: type: boolean default: true required: [name, schedule] UpdateScheduledJobRequest: type: object properties: name: type: string minLength: 2 schedule: type: string url: type: string format: uri function_id: type: string format: uuid nullable: true description: Set to retarget at a function; send "" to clear back to url delivery. secret: type: string description: Replace the signing secret. Omit to keep the current one. payload: type: object enabled: type: boolean ScheduledJobCreateResponse: allOf: - $ref: "#/components/schemas/ScheduledJob" - type: object properties: secret: type: string description: Plaintext secret — present only when Primora generated it. Store it; it cannot be recovered. ScheduledJobListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/ScheduledJob" required: [items] ScheduledJobRun: type: object properties: id: type: string format: uuid job_id: type: string format: uuid status: type: string enum: [running, success, failed] triggered_by: type: string enum: [schedule, manual] status_code: type: integer nullable: true error: type: string duration_ms: type: integer nullable: true started_at: type: string format: date-time finished_at: type: string format: date-time nullable: true required: [id, job_id, status, triggered_by] ScheduledJobRunListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/ScheduledJobRun" required: [items] ProjectSecret: type: object properties: id: type: string format: uuid name: type: string description: Env-var-safe name; referenced as secret://NAME in job payloads. url: type: string notes: type: string created_at: type: string format: date-time updated_at: type: string format: date-time required: [id, name, url, notes, created_at, updated_at] ProjectSecretListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/ProjectSecret" required: [items] SetProjectSecretRequest: type: object properties: value: type: string description: Plaintext — stored AES-256-GCM encrypted, never returned by list. url: type: string notes: type: string required: [value] RevealProjectSecretResponse: type: object properties: name: type: string value: type: string required: [name, value] AlertRuleConfig: type: object properties: component: type: string description: Restrict the rule to one component name; empty = all components. threshold_minutes: type: integer description: heartbeat_silence — fire when a component has not heartbeated for this many minutes (1–10080). threshold_count: type: integer format: int64 description: error_spike — fire when a component logs at least this many errors inside the window. window_minutes: type: integer description: error_spike — sliding window size (default 15, 1–1440). AlertRule: type: object properties: id: type: string format: uuid project_id: type: string format: uuid name: type: string kind: type: string enum: [heartbeat_silence, error_spike] config: $ref: "#/components/schemas/AlertRuleConfig" enabled: type: boolean firing: type: array items: type: string description: Component names currently in breached state. created_at: type: string format: date-time updated_at: type: string format: date-time required: [id, project_id, name, kind, config, enabled, firing, created_at, updated_at] AlertRuleListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/AlertRule" required: [items] UpsertAlertRuleRequest: type: object properties: name: type: string description: Env-var-safe name ([A-Za-z_][A-Za-z0-9_]*). kind: type: string enum: [heartbeat_silence, error_spike] config: $ref: "#/components/schemas/AlertRuleConfig" enabled: type: boolean required: [name, kind] InboundHook: type: object properties: id: type: string format: uuid project_id: type: string format: uuid name: type: string token: type: string url: type: string description: Full ingest URL — POST here from the external system. mode: type: string enum: [event, job, function] job_id: type: string function_id: type: string has_secret: type: boolean enabled: type: boolean last_received_at: type: string format: date-time nullable: true created_at: type: string format: date-time required: [id, project_id, name, token, url, mode, has_secret, enabled, created_at] InboundHookListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/InboundHook" required: [items] CreateInboundHookRequest: type: object properties: name: type: string mode: type: string enum: [event, job, function] default: event job_id: type: string description: Required when mode=job — scheduled job to trigger. function_id: type: string description: Required when mode=function — invoked with the received body. secret: type: string description: Optional — stored encrypted; callers must then sign bodies with X-Primora-Signature. enabled: type: boolean required: [name] InboundReceiveResponse: type: object properties: received: type: boolean event: type: string run_id: type: string required: [received] EmailLogEntry: type: object properties: id: type: string format: uuid template: type: string to_email: type: string subject: type: string status: type: string enum: [sent, failed] error: type: string created_at: type: string format: date-time required: [id, template, to_email, subject, status, created_at] EmailLogListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/EmailLogEntry" required: [items] Function: type: object properties: id: type: string format: uuid name: type: string runtime: type: string enum: [bun, deno] enabled: type: boolean event_pattern: type: string description: 'Domain-event pattern that triggers this function — "document.*", "*", exact type. Empty = never.' created_at: type: string format: date-time updated_at: type: string format: date-time required: [id, name, runtime, enabled, created_at, updated_at] FunctionListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/Function" required: [items] FunctionDetailResponse: type: object properties: function: $ref: "#/components/schemas/Function" code: type: string required: [function, code] CreateFunctionRequest: type: object properties: name: type: string code: type: string runtime: type: string enum: [bun, deno] default: bun event_pattern: type: string description: 'Optional — "document.*", "*", or an exact event type.' required: [name, code] UpdateFunctionRequest: type: object properties: code: type: string enabled: type: boolean event_pattern: type: string description: Replace the trigger pattern; "" disables event triggers. InvokeFunctionRequest: type: object properties: payload: {} FunctionRun: type: object properties: id: type: string format: uuid trigger: type: string enum: [manual, schedule, hook, event] status: type: string enum: [success, error, timeout] exit_code: type: integer nullable: true stdout: type: string stderr: type: string duration_ms: type: integer created_at: type: string format: date-time required: [id, trigger, status, stdout, stderr, duration_ms, created_at] FunctionRunListResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/FunctionRun" required: [items]