openapi: 3.1.0 info: title: Silicon Remind API version: 0.1.0 description: Durable Silicon reminders with organization-wide read access, owner-Silicon mutations, IAM application sessions and isolated test environments. Test requests use X-Remind-Test-Key plus a test IAM identity; they never fall through to production. servers: - url: https://backend.remind.teamofsilicons.com/api/v1 security: - bearerAuth: [] tags: - name: Schedules - name: Executions paths: /reports: post: operationId: submitBugReport summary: Queue a bug report email, or simulate delivery in testing description: Ten reports per actor per hour. Idempotent retries return the original receipt. Requires Postmark configuration in production. parameters: - $ref: '#/components/parameters/OrgId' - $ref: '#/components/parameters/TestKey' - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BugReportRequest' responses: '202': description: Queued or simulated receipt content: application/json: schema: $ref: '#/components/schemas/BugReportResponse' '200': description: Idempotent replay with the latest delivery state content: application/json: schema: $ref: '#/components/schemas/BugReportResponse' default: description: Authentication, validation, rate limit, idempotency or dependency error content: application/json: schema: $ref: '#/components/schemas/Error' /reports/{id}: get: operationId: getBugReport summary: Read your own bug report delivery status parameters: - $ref: '#/components/parameters/OrgId' - $ref: '#/components/parameters/TestKey' - name: id in: path required: true schema: {type: string, format: uuid} responses: '200': description: Queued, sending, sent, failed, or simulated content: application/json: schema: $ref: '#/components/schemas/BugReportResponse' default: description: Standard API error content: application/json: schema: $ref: '#/components/schemas/Error' /telemetry/events: post: operationId: recordTelemetryEvent summary: Record an operational event in the selected environment description: Best effort, enabled by default. Testing records remain in the sandbox. Credentials, text, URLs and arbitrary context are rejected. parameters: - $ref: '#/components/parameters/OrgId' - $ref: '#/components/parameters/TestKey' - name: X-Remind-Telemetry in: header schema: {type: string, enum: ['on', 'off']} requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TelemetryEvent' responses: '204': description: Event processed, or ignored after opt-out default: description: Standard API error content: application/json: schema: $ref: '#/components/schemas/Error' /api/versions: servers: - url: https://backend.remind.teamofsilicons.com get: operationId: discoverVersions summary: Discover implemented wire versions and deprecation state security: [] responses: '200': description: Current and supported API contracts, protocol and seven-day idle sunset policy content: application/json: schema: type: object additionalProperties: true /schedules: get: tags: - Schedules operationId: listSchedules summary: List schedules visible to the current actor description: Current or archived reminders in the authenticated organization, filtered before cursor pagination. Carbons are read-only; only an owning Silicon may mutate reminders. parameters: - $ref: '#/components/parameters/OrgId' - name: silicon_id in: query schema: $ref: '#/components/schemas/SiliconGlobalId' - name: section in: query description: Product-facing reminder section; current is the default. schema: allOf: - $ref: '#/components/schemas/ScheduleSection' default: current - name: status in: query schema: $ref: '#/components/schemas/ScheduleStatus' - $ref: '#/components/parameters/Cursor' - $ref: '#/components/parameters/Limit' - $ref: '#/components/parameters/TestKey' responses: '200': description: Schedules content: application/json: schema: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/Schedule' next_cursor: type: - string - 'null' default: $ref: '#/components/responses/Error' patch: tags: - Schedules operationId: updateScheduleStatuses summary: Atomically pause or resume owned current schedules description: Applies one active or paused status to every requested schedule in the supplied order. The authenticated Silicon must own every schedule, and every schedule must still be current. Validation is all-or-nothing; failures leave the complete batch unchanged. The response contains one compact status result per requested schedule in request order. parameters: - $ref: '#/components/parameters/OrgId' - $ref: '#/components/parameters/IdempotencyKey' - $ref: '#/components/parameters/TestKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ScheduleStatusBatchUpdate' responses: '200': description: Schedule status results in request order content: application/json: schema: $ref: '#/components/schemas/ScheduleStatusBatchUpdateResponse' default: $ref: '#/components/responses/Error' post: tags: - Schedules operationId: createSchedule summary: Create a one-time or recurring schedule for the authenticated Silicon parameters: - $ref: '#/components/parameters/OrgId' - $ref: '#/components/parameters/IdempotencyKey' - $ref: '#/components/parameters/TestKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ScheduleCreate' responses: '201': description: Schedule created content: application/json: schema: $ref: '#/components/schemas/Schedule' default: $ref: '#/components/responses/Error' /schedules/{schedule_id}: parameters: - $ref: '#/components/parameters/OrgId' - $ref: '#/components/parameters/ScheduleId' get: tags: - Schedules operationId: getSchedule summary: Get an IAM-visible schedule description: Inaccessible identifiers return the same not-found response as absent identifiers. responses: '200': description: Schedule content: application/json: schema: $ref: '#/components/schemas/Schedule' default: $ref: '#/components/responses/Error' parameters: - $ref: '#/components/parameters/TestKey' patch: tags: - Schedules operationId: updateSchedule summary: Update a schedule owned by the authenticated Silicon parameters: - $ref: '#/components/parameters/IdempotencyKey' - $ref: '#/components/parameters/TestKey' requestBody: required: true content: application/json: schema: type: object minProperties: 1 additionalProperties: false properties: text: type: string minLength: 1 timezone: type: string description: IANA time-zone identifier kind: $ref: '#/components/schemas/ScheduleKind' cron: type: string description: Five-field Linux cron expression status: $ref: '#/components/schemas/MutableScheduleStatus' responses: '200': description: Updated schedule content: application/json: schema: $ref: '#/components/schemas/Schedule' default: $ref: '#/components/responses/Error' delete: tags: - Schedules operationId: archiveSchedule summary: Archive a schedule owned by the authenticated Silicon for 45 days responses: '204': description: Archived, or already archived default: $ref: '#/components/responses/Error' parameters: - $ref: '#/components/parameters/TestKey' /schedules/{schedule_id}/executions: get: tags: - Executions operationId: listScheduleExecutions summary: List execution history for an IAM-visible schedule description: The parent schedule's IAM-projected owner visibility is enforced before pagination. parameters: - $ref: '#/components/parameters/OrgId' - $ref: '#/components/parameters/ScheduleId' - $ref: '#/components/parameters/Cursor' - $ref: '#/components/parameters/Limit' - $ref: '#/components/parameters/TestKey' responses: '200': description: Execution history content: application/json: schema: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/Execution' next_cursor: type: - string - 'null' default: $ref: '#/components/responses/Error' /auth/login: post: operationId: authLogin summary: Exchange an IAM short-lived token parameters: - $ref: '#/components/parameters/TestKey' responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/Session' default: &id001 $ref: '#/components/responses/Error' requestBody: required: true content: application/json: schema: type: object properties: slt: &id002 type: string writeOnly: true required: - slt security: [] description: 'App credentials remain on the server. Responses containing tokens use Cache-Control: no-store. In a sandbox all IAM requests carry the linked IAM environment key and test Application credential.' /auth/iam: get: operationId: getIamInfo summary: Discover public IAM application configuration before login description: Returns the selected deployment's app ID, IAM URL and linked IAM test environment UUID (null in production). No app secrets or environment keys are returned. Uses Cache-Control no-store. security: [] parameters: - $ref: '#/components/parameters/TestKey' responses: '200': description: Public IAM configuration content: application/json: schema: type: object required: [app_id, iam_url, iam_environment_id] properties: app_id: type: string iam_url: type: string format: uri iam_environment_id: type: [string, 'null'] format: uuid default: $ref: '#/components/responses/Error' /auth/refresh: post: operationId: authRefresh summary: Rotate an application refresh token parameters: - $ref: '#/components/parameters/TestKey' responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/Session' default: *id001 requestBody: required: true content: application/json: schema: type: object properties: refresh_token: *id002 required: - refresh_token security: [] description: 'App credentials remain on the server. Responses containing tokens use Cache-Control: no-store. In a sandbox all IAM requests carry the linked IAM environment key and test Application credential.' /auth/logout: post: operationId: authLogout summary: Revoke an application token or refresh family parameters: - $ref: '#/components/parameters/TestKey' responses: '204': description: Success default: *id001 requestBody: required: true content: application/json: schema: type: object properties: token: *id002 required: - token security: [] description: 'App credentials remain on the server. Responses containing tokens use Cache-Control: no-store. In a sandbox all IAM requests carry the linked IAM environment key and test Application credential.' /auth/organizations: get: operationId: listAuthorizedOrganizations summary: List organizations currently authorized through IAM description: Lists only IAM-selected organizations with active membership. No X-Org-ID is required. Unscoped access tokens can use an organization from this list as X-Org-ID for ordinary reminder requests. Legacy scoped sessions return their single authorized organization. parameters: - $ref: '#/components/parameters/TestKey' responses: '200': description: Current authorized organization identities; empty when none are authorized content: application/json: schema: type: object required: [items] properties: items: type: array items: $ref: '#/components/schemas/Identity' default: *id001 /auth/me: get: operationId: getIdentity summary: Read current IAM organization authority parameters: - &id003 $ref: '#/components/parameters/OrgId' - $ref: '#/components/parameters/TestKey' responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/Identity' default: *id001 /webhook: get: operationId: getWebhook summary: Read the owning Silicon delivery endpoint parameters: - *id003 - $ref: '#/components/parameters/TestKey' responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/DestinationInfo' default: *id001 put: operationId: configureWebhook summary: Configure the owning configured webhook endpoint parameters: - *id003 - $ref: '#/components/parameters/TestKey' responses: '201': &id004 description: Success content: application/json: schema: $ref: '#/components/schemas/DestinationReceipt' default: *id001 '200': *id004 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DestinationInput' delete: operationId: disableWebhook summary: Disable the owning Silicon delivery endpoint parameters: - *id003 - $ref: '#/components/parameters/TestKey' responses: '204': description: Success default: *id001 /webhooks: get: operationId: listWebhooks summary: List the owning Silicon's webhook subscriptions parameters: - *id003 - $ref: '#/components/parameters/TestKey' responses: '200': description: Active subscriptions, possibly empty content: application/json: schema: $ref: '#/components/schemas/WebhookSubscriptions' default: *id001 post: operationId: subscribeWebhook summary: Add a webhook subscription parameters: - *id003 - $ref: '#/components/parameters/TestKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DestinationInput' responses: '201': description: Subscription created content: application/json: schema: $ref: '#/components/schemas/DestinationReceipt' default: *id001 /webhooks/{subscription_id}: delete: operationId: unsubscribeWebhook summary: Disable one webhook subscription parameters: - *id003 - $ref: '#/components/parameters/TestKey' - name: subscription_id in: path required: true schema: type: string format: uuid responses: '204': description: Subscription disabled default: *id001 /silicons: get: operationId: listSilicons summary: List registered Silicons in the organization parameters: - *id003 - &id005 name: after in: query schema: &id007 type: string format: uuid - &id006 name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 50 - $ref: '#/components/parameters/TestKey' responses: '200': description: Success content: application/json: schema: type: object properties: items: type: array items: $ref: '#/components/schemas/Silicon' next_cursor: type: - string - 'null' required: - items default: *id001 /test-environments: get: operationId: listTestEnvironments summary: List organization-owned environments parameters: - *id003 - *id005 - *id006 - name: include_deleted in: query schema: type: boolean default: false responses: '200': description: Success content: application/json: schema: type: object properties: items: type: array items: $ref: '#/components/schemas/TestEnvironment' next_cursor: type: - string - 'null' required: - items default: *id001 post: operationId: createTestEnvironment summary: Create an empty IAM-bound test environment parameters: - *id003 responses: '201': description: Success content: application/json: schema: $ref: '#/components/schemas/EnvironmentCreated' default: *id001 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateTestEnvironment' /test-environments/{id}: get: operationId: getTestEnvironment summary: Read environment metadata parameters: - *id003 - &id008 name: id in: path required: true schema: *id007 responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/TestEnvironment' default: *id001 delete: operationId: deleteTestEnvironment summary: Retire an environment for 30-day recovery parameters: - *id003 - *id008 responses: '204': description: Success default: *id001 /test-environments/{id}/key: get: operationId: getEnvironmentKey summary: Retrieve the active key parameters: - *id003 - *id008 responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/EnvironmentKey' default: *id001 description: 'Restricted to the creator or a current organization owner/admin. Keys are retrievable, encrypted at rest and returned with Cache-Control: no-store.' /test-environments/{id}/key-rotations: post: operationId: rotateEnvironmentKey summary: Rotate and revoke the previous key parameters: - *id003 - *id008 responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/EnvironmentKey' default: *id001 description: 'Restricted to the creator or a current organization owner/admin. Keys are retrievable, encrypted at rest and returned with Cache-Control: no-store.' /test-environments/{id}/restorations: post: operationId: restoreEnvironment summary: Recover a deleted environment with a new key parameters: - *id003 - *id008 responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/EnvironmentKey' default: *id001 description: 'Restricted to the creator or a current organization owner/admin. Keys are retrievable, encrypted at rest and returned with Cache-Control: no-store.' /testing-environment: get: operationId: currentEnvironment summary: Describe the selected sandbox parameters: [] responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/TestEnvironment' default: *id001 security: - testKeyAuth: [] description: Requires the Remind environment key only. Omitting it returns test_environment_required. Cleaning does not clean the linked IAM environment. A sandbox permits at most 100 retained reminders; inactivity retires it after 15 days. /testing-environment/cleanings: post: operationId: cleanEnvironment summary: Atomically clear all sandbox data, keeping the IAM binding and root key parameters: [] responses: '204': description: Success default: *id001 security: - testKeyAuth: [] description: Requires the Remind environment key only. Omitting it returns test_environment_required. Cleaning does not clean the linked IAM environment. A sandbox permits at most 100 retained reminders; inactivity retires it after 15 days. /health/live: get: operationId: healthLive summary: Check live status parameters: [] responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/Health' default: *id001 security: [] servers: - url: https://backend.remind.teamofsilicons.com /health/ready: get: operationId: healthReady summary: Check ready status parameters: [] responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/Health' default: *id001 security: [] servers: - url: https://backend.remind.teamofsilicons.com /webhook/: post: operationId: receiveIamWebhook summary: Receive exact-byte signed IAM lifecycle events parameters: - name: X-Silicon-IAM-Event-Id in: header required: true schema: &id009 type: string - name: X-Silicon-IAM-Timestamp in: header required: true schema: *id009 - name: X-Silicon-IAM-Key-Version in: header required: true schema: *id009 - name: X-Silicon-IAM-Signature in: header required: true schema: *id009 responses: '202': description: Success content: application/json: schema: type: object properties: receipt_id: *id007 status: *id009 required: - receipt_id - status default: *id001 requestBody: required: true content: application/json: schema: type: object security: [] servers: - url: https://backend.remind.teamofsilicons.com description: Authenticates X-Silicon-IAM-Event-Id, X-Silicon-IAM-Timestamp, X-Silicon-IAM-Key-Version and X-Silicon-IAM-Signature through the official verifier. Testing envelopes route only to linked active Remind sandboxes after signature and IAM-key verification. Root keys are never persisted in event payloads. /testing-environment/iam: put: operationId: configureEnvironmentIam summary: Install or rotate the sandbox IAM Application credential security: [] parameters: - $ref: '#/components/parameters/TestKey' requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: - iam_app_secret properties: iam_app_secret: type: string writeOnly: true responses: '204': description: Credential verified in the bound IAM test world and encrypted at rest default: $ref: '#/components/responses/Error' components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: IAM Remind-audience application token testKeyAuth: type: apiKey in: header name: X-Remind-Test-Key description: 32-character Remind root key. Selects an isolated test environment; ordinary actions still require a test IAM access token. parameters: OrgId: name: X-Org-ID in: header required: true schema: $ref: '#/components/schemas/OrgId' ScheduleId: name: schedule_id in: path required: true schema: type: string format: uuid IdempotencyKey: name: Idempotency-Key in: header required: true schema: type: string minLength: 8 maxLength: 255 Cursor: name: cursor in: query schema: type: string Limit: name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 20 TestKey: name: X-Remind-Test-Key in: header required: false description: IAM sandbox application app_secret (ask_...) or legacy Remind key. Invalid, expired or revoked keys fail closed. Environment management routes reject this header. schema: type: string pattern: ^(?:[a-zA-Z0-9]{32}|ask_[a-zA-Z0-9_-]{43})$ responses: WebhookNotConfigured: description: The requested legacy subscription does not exist or is inactive. content: application/json: schema: $ref: '#/components/schemas/Error' example: error: code: webhook_not_configured message: No active webhook subscription is configured. request_id: 0199d8d5-2f62-7eb7-81c5-1e456778f8de Error: description: Error content: application/json: schema: $ref: '#/components/schemas/Error' schemas: BugReportRequest: type: object additionalProperties: false required: [message] properties: message: type: string minLength: 1 maxLength: 16384 description: Reproduction details, maximum 16384 UTF-8 bytes. Exclude credentials. pr: type: [string, 'null'] pattern: '^https://github.com/teamofsilicons/silicon-remind/pull/[1-9][0-9]*$' BugReportResponse: type: object required: [id, status, failure_reason] properties: id: {type: string, format: uuid} status: {type: string, enum: [queued, sending, sent, failed, simulated]} failure_reason: {type: [string, 'null']} TelemetryEvent: type: object additionalProperties: false required: [source, event, step, success, duration_ms] properties: source: {type: string, enum: [rust_client, cli, daemon, web]} event: {type: string, enum: [request_completed, command_completed, update_completed, heartbeat, page_view, navigation, interaction, error, performance]} step: {type: string, enum: [response, command, maintenance, browser]} success: {type: boolean} duration_ms: {type: integer, minimum: 0, maximum: 86400000} status_code: {type: [integer, 'null'], minimum: 100, maximum: 599} OrgId: type: string pattern: ^[a-z0-9_-]{3,50}$ SiliconGlobalId: type: string pattern: ^[a-z0-9_-]{3,50}:[a-z0-9_-]{3,50}$ ScheduleStatus: type: string enum: - active - paused - completed ScheduleSection: type: string enum: - current - archived MutableScheduleStatus: type: string enum: - active - paused ScheduleStatusBatchUpdate: type: object required: - schedule_ids - status additionalProperties: false properties: schedule_ids: type: array minItems: 1 maxItems: 100 uniqueItems: true items: type: string format: uuid status: $ref: '#/components/schemas/MutableScheduleStatus' ScheduleStatusBatchUpdateResponse: type: object required: - items additionalProperties: false properties: items: type: array minItems: 1 maxItems: 100 items: $ref: '#/components/schemas/ScheduleStatusResult' ScheduleStatusResult: type: object required: - id - status - next_run_at - updated_at additionalProperties: false properties: id: type: string format: uuid status: $ref: '#/components/schemas/MutableScheduleStatus' next_run_at: type: - string - 'null' format: date-time updated_at: type: string format: date-time ScheduleKind: type: string enum: - one_time - recurring ScheduleCreate: type: object required: - text - kind - cron additionalProperties: false properties: text: type: string minLength: 1 maxLength: 100000 kind: $ref: '#/components/schemas/ScheduleKind' cron: type: string description: Five-field Linux cron expression timezone: type: string default: UTC description: IANA time-zone identifier examples: - Asia/Kolkata Schedule: allOf: - $ref: '#/components/schemas/ScheduleCreate' - type: object required: - id - org_id - silicon_id - timezone - status - section - next_run_at - archived_at - purge_after - created_at - updated_at properties: id: type: string format: uuid org_id: $ref: '#/components/schemas/OrgId' silicon_id: $ref: '#/components/schemas/SiliconGlobalId' status: $ref: '#/components/schemas/ScheduleStatus' section: $ref: '#/components/schemas/ScheduleSection' next_run_at: type: - string - 'null' format: date-time archived_at: type: - string - 'null' format: date-time purge_after: type: - string - 'null' format: date-time created_at: type: string format: date-time updated_at: type: string format: date-time Execution: type: object required: - id - schedule_id - scheduled_for - status properties: id: type: string format: uuid description: Stable idempotency identifier for this occurrence schedule_id: type: string format: uuid scheduled_for: type: string format: date-time attempted_at: type: - string - 'null' format: date-time delivered_at: type: - string - 'null' format: date-time status: type: string enum: - pending - delivered - retrying - failed hook_event_id: type: - string - 'null' format: uuid failure_reason: type: - string - 'null' Error: type: object required: - error properties: error: type: object required: - code - message - request_id properties: code: type: string message: type: string request_id: type: string Session: type: object properties: access_token: *id009 refresh_token: *id009 expires_in: type: integer token_type: *id009 scope: *id009 actor: type: object org_id: type: - string - 'null' required: - access_token - refresh_token - expires_in - token_type - scope - actor Identity: type: object properties: principal_id: *id007 actor_type: type: string enum: - carbon - silicon public_id: type: - string - 'null' org_id: *id009 membership_id: *id007 org_role: type: - string - 'null' authorization_epoch: type: integer minimum: 0 can_manage_reminders: type: boolean required: - principal_id - actor_type - public_id - org_id - membership_id - org_role - authorization_epoch - can_manage_reminders DestinationInput: type: object properties: endpoint_url: type: string format: uri signing_secret: type: string maxLength: 4096 description: Optional HMAC signing secret; omit for unsigned delivery. required: - endpoint_url DestinationReceipt: type: object properties: id: type: string format: uuid silicon_id: *id009 version: &id010 type: integer updated_at: &id011 type: string format: date-time required: - id - silicon_id - version - updated_at DestinationInfo: type: object properties: id: type: string format: uuid silicon_id: *id009 version: *id010 updated_at: *id011 endpoint_url: type: string format: uri required: - id - silicon_id - version - updated_at - endpoint_url WebhookSubscriptions: type: object properties: items: type: array items: $ref: '#/components/schemas/DestinationInfo' required: - items Silicon: type: object properties: principal_id: *id007 silicon_id: *id009 reminder_count: type: integer minimum: 0 required: - principal_id - silicon_id - reminder_count TestEnvironment: type: object properties: id: *id007 org_id: *id009 creator_id: type: string name: *id009 description: type: - string - 'null' iam_environment_id: *id007 version: type: integer created_at: *id011 last_activity_at: *id011 deleted_at: &id012 type: - string - 'null' format: date-time purge_after: *id012 required: - id - org_id - creator_id - name - description - iam_environment_id - version - created_at - last_activity_at - deleted_at - purge_after CreateTestEnvironment: type: object properties: name: type: string minLength: 1 maxLength: 100 description: type: - string - 'null' maxLength: 10000 iam_test_key: type: string pattern: ^(?:[a-zA-Z0-9]{32}|ask_[a-zA-Z0-9_-]{43})$ writeOnly: true iam_app_secret: *id002 required: - name - iam_test_key EnvironmentCreated: type: object properties: environment: $ref: '#/components/schemas/TestEnvironment' key: type: string pattern: ^(?:[a-zA-Z0-9]{32}|ask_[a-zA-Z0-9_-]{43})$ required: - environment - key EnvironmentKey: type: object properties: environment_id: *id007 key: type: string pattern: ^(?:[a-zA-Z0-9]{32}|ask_[a-zA-Z0-9_-]{43})$ required: - environment_id - key Health: type: object properties: service: *id009 status: *id009 version: *id009 required: - service - status - version