openapi: 3.0.3 info: title: Insforge Secrets API version: 1.0.0 description: Secure secrets management for storing API keys, tokens, and sensitive configuration paths: /api/secrets: get: summary: List all secrets description: Returns metadata for all secrets (without values) tags: - Admin security: - bearerAuth: [] responses: '200': description: List of secret metadata content: application/json: schema: type: object properties: secrets: type: array items: $ref: '#/components/schemas/SecretMetadata' example: secrets: - id: "123e4567-e89b-12d3-a456-426614174000" key: "STRIPE_API_KEY" isActive: true isReserved: false createdAt: "2024-01-21T10:30:00Z" updatedAt: "2024-01-21T10:30:00Z" expiresAt: null - id: "223e4567-e89b-12d3-a456-426614174001" key: "OPENAI_API_KEY" isActive: true isReserved: true createdAt: "2024-01-20T09:15:00Z" updatedAt: "2024-01-20T09:15:00Z" expiresAt: "2025-01-20T09:15:00Z" '401': description: Unauthorized '403': description: Forbidden - Admin only post: summary: Create a new secret description: Create a new encrypted secret with a unique key tags: - Admin security: - bearerAuth: [] requestBody: required: true content: application/json: schema: type: object required: - key - value properties: key: type: string pattern: '^[A-Z0-9_]+$' description: Unique key identifier (uppercase letters, numbers, underscores only) example: "STRIPE_API_KEY" value: type: string description: Secret value to be encrypted example: "sk_live_..." isReserved: type: boolean default: false description: Whether the secret is protected from deletion expiresAt: type: string format: date-time nullable: true description: Optional expiration date for the secret responses: '201': description: Secret created successfully content: application/json: schema: type: object properties: success: type: boolean message: type: string id: type: string format: uuid example: success: true message: "Secret STRIPE_API_KEY has been created successfully" id: "123e4567-e89b-12d3-a456-426614174000" '400': description: Invalid input content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: missingFields: value: error: "INVALID_INPUT" message: "Both key and value are required" statusCode: 400 invalidKeyFormat: value: error: "INVALID_INPUT" message: "Invalid key format. Use uppercase letters, numbers, and underscores only (e.g., STRIPE_API_KEY)" statusCode: 400 '409': description: Secret already exists content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: error: "INVALID_INPUT" message: "Secret already exists: STRIPE_API_KEY" statusCode: 409 /api/secrets/{key}: get: summary: Get secret value description: Retrieve the decrypted value of a specific secret by its key tags: - Admin security: - bearerAuth: [] parameters: - name: key in: path required: true schema: type: string pattern: '^[A-Z0-9_]+$' description: Secret key identifier example: "STRIPE_API_KEY" responses: '200': description: Secret value retrieved content: application/json: schema: type: object properties: key: type: string value: type: string example: key: "STRIPE_API_KEY" value: "sk_live_..." '404': description: Secret not found content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: error: "NOT_FOUND" message: "Secret not found: INVALID_KEY" statusCode: 404 '401': description: Unauthorized '403': description: Forbidden - Admin only put: summary: Update secret description: Update an existing secret's value or metadata tags: - Admin security: - bearerAuth: [] parameters: - name: key in: path required: true schema: type: string pattern: '^[A-Z0-9_]+$' description: Secret key identifier example: "STRIPE_API_KEY" requestBody: required: true content: application/json: schema: type: object properties: value: type: string description: New secret value (will be encrypted) isActive: type: boolean description: Whether the secret is active isReserved: type: boolean description: Whether the secret is protected from deletion expiresAt: type: string format: date-time nullable: true description: Expiration date (null to remove expiration) responses: '200': description: Secret updated successfully content: application/json: schema: type: object properties: success: type: boolean message: type: string example: success: true message: "Secret STRIPE_API_KEY has been updated successfully" '404': description: Secret not found content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: error: "NOT_FOUND" message: "Secret not found: INVALID_KEY" statusCode: 404 '500': description: Failed to update secret content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: error: "INTERNAL_ERROR" message: "Failed to update secret: STRIPE_API_KEY" statusCode: 500 delete: summary: Delete secret description: Mark a secret as inactive (soft delete). Cannot delete reserved secrets. tags: - Admin security: - bearerAuth: [] parameters: - name: key in: path required: true schema: type: string pattern: '^[A-Z0-9_]+$' description: Secret key identifier example: "STRIPE_API_KEY" responses: '200': description: Secret deleted successfully content: application/json: schema: type: object properties: success: type: boolean message: type: string example: success: true message: "Secret STRIPE_API_KEY has been deleted successfully" '403': description: Cannot delete reserved secret content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: error: "FORBIDDEN" message: "Cannot delete reserved secret: OPENAI_API_KEY" statusCode: 403 '404': description: Secret not found content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: error: "NOT_FOUND" message: "Secret not found: INVALID_KEY" statusCode: 404 /api/secrets/anon-key/rotate: post: summary: Rotate anon key description: >- Rotate the project's opaque anon key (`anon_...`). A new key is generated and returned; the old key stays valid for the grace period (default 168 hours / 7 days, max 720) so already-deployed frontends and mobile binaries keep working while the new key ships. Admin only. tags: - Admin security: - bearerAuth: [] requestBody: required: false content: application/json: schema: type: object properties: gracePeriodHours: type: integer minimum: 0 maximum: 720 default: 168 description: How long the old key remains valid after rotation responses: '200': description: Anon key rotated successfully content: application/json: schema: type: object properties: success: type: boolean message: type: string anonKey: type: string description: The new anon key oldKeyExpiresAt: type: string format: date-time description: When the previous key stops being accepted example: success: true message: "Anon key rotated successfully. Old key will remain valid during grace period." anonKey: "anon_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd" oldKeyExpiresAt: "2026-06-18T00:00:00.000Z" '400': description: Invalid grace period content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden - admin access required content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT schemas: SecretMetadata: type: object properties: id: type: string format: uuid description: Unique identifier for the secret key: type: string description: Unique key identifier (uppercase with underscores) example: "STRIPE_API_KEY" isActive: type: boolean description: Whether the secret is currently active isReserved: type: boolean description: Whether the secret is protected from deletion createdAt: type: string format: date-time description: When the secret was created updatedAt: type: string format: date-time description: When the secret was last updated expiresAt: type: string format: date-time nullable: true description: When the secret expires (null if no expiration) required: - id - key - isActive - isReserved - createdAt - updatedAt ErrorResponse: type: object required: - error - message - statusCode properties: error: type: string description: Error code for programmatic handling example: "INVALID_INPUT" message: type: string description: Human-readable error message example: "Invalid input data" statusCode: type: integer description: HTTP status code example: 400 nextActions: type: string description: Suggested action to resolve the error example: "Check the request body format"