From 2ce61be544a9ea91d33eed321e9bafe6503b85bc Mon Sep 17 00:00:00 2001 From: dawnho Date: Thu, 2 Jul 2026 11:55:42 -0700 Subject: [PATCH] feat: honor paths.yaml include_groups in generated error pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Mintlify error/warning page generator ignored the `include_groups` filter in codegen/data/paths.yaml, so its pages diverged from the GitBook API reference in two ways: - Device sub-category pages (locks, thermostats) had no page of their own, or once added listed only each type's specific variant group. GitBook showed the universal (ungrouped) device codes plus the type's configured groups — e.g. thermostats had 23 codes (device_offline, account_disconnected, …), not just the 2 thermostat-specific ones; locks had 39, not 10. - Resource pages that DO have a filter (access_codes, access_codes/unmanaged) over-included: they showed every group, surfacing a stray `thermostats` group code (`auxiliary_heat_running`) that GitBook excluded. paths.yaml is already loaded by the Mintlify pipeline as pathMetadata, so this drives the exact same config via one rule mirroring the legacy groupVariants: include_groups set → keep universal codes + listed groups, flattened into one code-sorted list; unset → keep every group with its subheading. Applied to: - /locks → universal + locks + access_codes groups (10 → 39) - /thermostats → universal + thermostats group (2 → 23) - /access_codes, /access_codes/unmanaged → drop the stray thermostats code and flatten (now match GitBook exactly, 35 codes each) - /phones → unchanged (its own group's warning; no include_groups upstream, and a phone is distinct from a lock/thermostat) Generated code sets now match the GitBook pages exactly. Resources without a filter (devices, acs/*, connected_accounts, …) are unchanged. Co-Authored-By: Claude Opus 4.8 --- mintlify-codegen/errors.ts | 94 +++++---- mintlify-codegen/generate.ts | 2 +- mintlify-codegen/load-data.ts | 6 + mintlify-docs/api/access_codes/errors.mdx | 60 +++--- .../api/access_codes/unmanaged/errors.mdx | 60 +++--- mintlify-docs/api/locks/errors.mdx | 194 +++++++++++++++++- mintlify-docs/api/thermostats/errors.mdx | 145 ++++++++++++- 7 files changed, 444 insertions(+), 117 deletions(-) diff --git a/mintlify-codegen/errors.ts b/mintlify-codegen/errors.ts index 80d1271c9..c38d649dd 100644 --- a/mintlify-codegen/errors.ts +++ b/mintlify-codegen/errors.ts @@ -4,6 +4,7 @@ import { join } from 'node:path' import type { Blueprint, DiscriminatedListProperty } from '@seamapi/blueprint' +import type { PathMetadata } from './load-data.js' import { formatType, indent, sampleValue } from './property-fields.js' /** @@ -347,38 +348,35 @@ async function writeErrorPage( routes.push(routePath) } -// Some device sub-categories have their own docs route but no blueprint -// resource of their own — the Seam API models a lock, thermostat, or phone as a -// `device`. Their error/warning codes therefore live on the `device` resource -// under a variant group of the same key rather than on a resource of their own, -// so the per-resource loop never produces a page for them. This maps each such -// variant group to the route that should carry its dedicated page. -// -// The map is an explicit allowlist, not `'/' + groupKey`: groups like -// `access_codes`, `hardware`, and `provider_metadata` annotate the device page -// but have no standalone sub-category route. In particular /access_codes -// already documents the `access_code` resource's own, distinct errors — routing -// the device `access_codes` group there would clobber it. -const DEVICE_SUBCATEGORY_ERROR_ROUTES: Record = { - locks: '/locks', - phones: '/phones', - thermostats: '/thermostats', -} +// Route that surfaces the `phones` device variant group. A phone is documented +// as its own resource (`/phones`), but that resource has no error/warning codes +// of its own — the one phone code (`unknown_issue_with_phone`) lives on the +// `device` resource under the `phones` group. Unlike locks/thermostats it has no +// `include_groups` filter in paths.yaml, so it surfaces only that group's codes. +const PHONES_ROUTE_PATH = '/phones' /** - * Narrow a device `errors`/`warnings` property to a single variant group, - * flattening the kept variants to ungrouped (`variantGroupKey` cleared) so their - * codes render as top-level entries — a group heading would be redundant on a - * page already scoped to that sub-category. Returns undefined when the property - * is absent or the group has no variants, so the caller emits nothing. + * Filter a discriminated `errors`/`warnings` property to a subset of variant + * groups, flattening the kept variants into a single ungrouped, code-sorted + * list. Keeps the variants whose group is in `groups`, plus the universal + * (ungrouped) codes when `includeUngrouped` is set. This mirrors the GitBook API + * reference, which renders an `include_groups`-filtered view as one flat list + * rather than with per-group headings. Returns undefined when nothing matches, + * so the caller emits nothing. */ -function variantGroupProp( +function filterVariantGroups( prop: Property | undefined, - groupKey: string, + groups: readonly string[], + includeUngrouped: boolean, ): (Property & DiscriminatedListProperty) | undefined { if (!isDiscriminatedListProperty(prop)) return undefined + const keep = new Set(groups) const variants = prop.variants - .filter((v) => v.variantGroupKey === groupKey) + .filter((v) => + v.variantGroupKey == null + ? includeUngrouped + : keep.has(v.variantGroupKey), + ) .map((v) => ({ ...v, variantGroupKey: null })) if (variants.length === 0) return undefined return { ...prop, variants, variantGroups: [] } @@ -392,38 +390,64 @@ function variantGroupProp( export async function updateErrorPages( blueprint: Blueprint, docsDir: string, + pathMetadata: PathMetadata, ): Promise { const routes: string[] = [] for (const resource of blueprint.resources) { if (resource.isUndocumented) continue + // Honor an `include_groups` filter from paths.yaml (e.g. /access_codes lists + // only its `locks` and `access_codes` groups, dropping a stray `thermostats` + // code): keep the universal (ungrouped) codes plus the listed groups, + // flattened — matching the GitBook API reference. Resources without a filter + // keep every group and its subheading. + const include = pathMetadata[resource.routePath]?.include_groups + const pick = (name: string): Property | undefined => { + const prop = resource.properties.find((p) => p.name === name) + return include == null ? prop : filterVariantGroups(prop, include, true) + } await writeErrorPage( docsDir, resource.routePath, - resource.properties.find((p) => p.name === 'errors'), - resource.properties.find((p) => p.name === 'warnings'), + pick('errors'), + pick('warnings'), routes, ) } - // Pages for device sub-categories (locks, thermostats, …) that have their own - // docs route but no resource of their own: source each from the matching - // variant group on the device resource. + // Device sub-category pages. Locks and thermostats have their own docs route + // but no resource of their own (the Seam API models them as `device`s), so the + // per-resource loop never reaches them. Source their codes from the device + // resource, filtered the same way: + // - Routes that document `device` under an `include_groups` filter + // (thermostats, locks — see paths.yaml) show the universal device codes + // plus the listed groups. + // - Phones has no `include_groups` filter upstream and is a distinct concept + // from a lock/thermostat device, so it surfaces only its own group's code. const device = blueprint.resources.find((r) => r.resourceType === 'device') if (device != null) { const errorsProp = device.properties.find((p) => p.name === 'errors') const warningsProp = device.properties.find((p) => p.name === 'warnings') - for (const [groupKey, routePath] of Object.entries( - DEVICE_SUBCATEGORY_ERROR_ROUTES, - )) { + + for (const [routePath, meta] of Object.entries(pathMetadata)) { + if (!meta.resources?.includes('device')) continue + if (meta.include_groups == null) continue await writeErrorPage( docsDir, routePath, - variantGroupProp(errorsProp, groupKey), - variantGroupProp(warningsProp, groupKey), + filterVariantGroups(errorsProp, meta.include_groups, true), + filterVariantGroups(warningsProp, meta.include_groups, true), routes, ) } + + await writeErrorPage( + docsDir, + PHONES_ROUTE_PATH, + filterVariantGroups(errorsProp, ['phones'], false), + filterVariantGroups(warningsProp, ['phones'], false), + routes, + ) } return routes diff --git a/mintlify-codegen/generate.ts b/mintlify-codegen/generate.ts index 23901fbdf..7a323f7d5 100644 --- a/mintlify-codegen/generate.ts +++ b/mintlify-codegen/generate.ts @@ -97,7 +97,7 @@ await insertEventsPagesIntoNav(updatedEvents) // rewritten. Nav wiring runs after events so the sidebar reads // object -> events -> errors. console.log('Updating error and warning documentation...') -const updatedErrors = await updateErrorPages(blueprint, outputDir) +const updatedErrors = await updateErrorPages(blueprint, outputDir, pathMetadata) if (updatedErrors.length > 0) { console.log( ` Generated errors pages for ${updatedErrors.length} resources: ${updatedErrors.join(', ')}`, diff --git a/mintlify-codegen/load-data.ts b/mintlify-codegen/load-data.ts index 5fe2868e9..81ce504a1 100644 --- a/mintlify-codegen/load-data.ts +++ b/mintlify-codegen/load-data.ts @@ -16,6 +16,12 @@ interface PathMetadataEntry { description?: string overview?: string alpha?: string + // Resources this route documents (e.g. `device` for /thermostats and /locks, + // which have no resource of their own). + resources?: string[] + // Error/warning variant groups to surface on this route. When set, a filtered + // view shows the universal (ungrouped) codes plus only these groups. + include_groups?: string[] } export type PathMetadata = Record diff --git a/mintlify-docs/api/access_codes/errors.mdx b/mintlify-docs/api/access_codes/errors.mdx index 4b1505154..627d25ca9 100644 --- a/mintlify-docs/api/access_codes/errors.mdx +++ b/mintlify-docs/api/access_codes/errors.mdx @@ -84,6 +84,18 @@ Indicates that the account is disconnected. --- +### `august_lock_missing_bridge` + +Indicates that the lock is not connected to a bridge. + +--- + +### `august_lock_not_authorized` + +Indicates that the user is not authorized to use the August lock. + +--- + ### `bridge_disconnected` Indicates that the Seam API cannot communicate with [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge), for example, if the Seam Bridge executable has stopped or if the computer running the Seam Bridge executable is offline. See also [Troubleshooting Your Access Control System](https://docs.seam.co/low-level-apis/access-systems/troubleshooting-your-access-control-system#acs_system.errors.seam_bridge_disconnected). @@ -132,6 +144,12 @@ Duplicate access code detected on device. --- +### `empty_backup_access_code_pool` + +Indicates that the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is empty. + +--- + ### `failed_to_remove_from_device` Failed to remove code from device. @@ -186,6 +204,12 @@ This access code was overridden on the device by a newer access code programmed --- +### `salto_ks_subscription_limit_exceeded` + +Indicates that the Salto site user limit has been reached. + +--- + ### `salto_ks_user_not_subscribed` Salto site user is not subscribed. @@ -204,42 +228,6 @@ Indicates that the lock is not paired with a gateway. --- -### Access Codes - -#### `empty_backup_access_code_pool` - -Indicates that the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is empty. - ---- - -### Locks - -#### `august_lock_missing_bridge` - -Indicates that the lock is not connected to a bridge. - ---- - -#### `august_lock_not_authorized` - -Indicates that the user is not authorized to use the August lock. - ---- - -#### `salto_ks_subscription_limit_exceeded` - -Indicates that the Salto site user limit has been reached. - ---- - -### Thermostats - -#### `auxiliary_heat_running` - -Indicates that the auxiliary heat is running. - ---- - ## Warnings Each warning is an object with the following shape: diff --git a/mintlify-docs/api/access_codes/unmanaged/errors.mdx b/mintlify-docs/api/access_codes/unmanaged/errors.mdx index 1208914b2..10a29fe6f 100644 --- a/mintlify-docs/api/access_codes/unmanaged/errors.mdx +++ b/mintlify-docs/api/access_codes/unmanaged/errors.mdx @@ -84,6 +84,18 @@ Indicates that the account is disconnected. --- +### `august_lock_missing_bridge` + +Indicates that the lock is not connected to a bridge. + +--- + +### `august_lock_not_authorized` + +Indicates that the user is not authorized to use the August lock. + +--- + ### `bridge_disconnected` Indicates that the Seam API cannot communicate with [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge), for example, if the Seam Bridge executable has stopped or if the computer running the Seam Bridge executable is offline. See also [Troubleshooting Your Access Control System](https://docs.seam.co/low-level-apis/access-systems/troubleshooting-your-access-control-system#acs_system.errors.seam_bridge_disconnected). @@ -132,6 +144,12 @@ Duplicate access code detected on device. --- +### `empty_backup_access_code_pool` + +Indicates that the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is empty. + +--- + ### `failed_to_remove_from_device` Failed to remove code from device. @@ -186,6 +204,12 @@ This access code was overridden on the device by a newer access code programmed --- +### `salto_ks_subscription_limit_exceeded` + +Indicates that the Salto site user limit has been reached. + +--- + ### `salto_ks_user_not_subscribed` Salto site user is not subscribed. @@ -204,42 +228,6 @@ Indicates that the lock is not paired with a gateway. --- -### Access Codes - -#### `empty_backup_access_code_pool` - -Indicates that the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is empty. - ---- - -### Locks - -#### `august_lock_missing_bridge` - -Indicates that the lock is not connected to a bridge. - ---- - -#### `august_lock_not_authorized` - -Indicates that the user is not authorized to use the August lock. - ---- - -#### `salto_ks_subscription_limit_exceeded` - -Indicates that the Salto site user limit has been reached. - ---- - -### Thermostats - -#### `auxiliary_heat_running` - -Indicates that the auxiliary heat is running. - ---- - ## Warnings Each warning is an object with the following shape: diff --git a/mintlify-docs/api/locks/errors.mdx b/mintlify-docs/api/locks/errors.mdx index 3ab463a6f..428246fc6 100644 --- a/mintlify-docs/api/locks/errors.mdx +++ b/mintlify-docs/api/locks/errors.mdx @@ -9,8 +9,8 @@ Each error is an object with the following shape: ```json Example error { - "error_code": "salto_ks_subscription_limit_exceeded", - "message": "Indicates that the Salto site user limit has been reached.", + "error_code": "account_disconnected", + "message": "Indicates that the account is disconnected.", "created_at": "2025-01-01T00:00:00.000Z", "is_connected_account_error": true, "is_device_error": true @@ -33,6 +33,10 @@ Each error is an object with the following shape: Date and time at which Seam created the error. + + Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + + Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts/object) error. @@ -43,6 +47,12 @@ Each error is an object with the following shape: +### `account_disconnected` + +Indicates that the account is disconnected. + +--- + ### `august_lock_missing_bridge` Indicates that the lock is not connected to a bridge. @@ -55,20 +65,86 @@ Indicates that the user is not authorized to use the August lock. --- +### `bridge_disconnected` + +Indicates that the Seam API cannot communicate with [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge), for example, if the Seam Bridge executable has stopped or if the computer running the Seam Bridge executable is offline. See also [Troubleshooting Your Access Control System](https://docs.seam.co/low-level-apis/access-systems/troubleshooting-your-access-control-system#acs_system.errors.seam_bridge_disconnected). + +--- + +### `device_disconnected` + +Indicates that the device is disconnected. + +--- + +### `device_offline` + +Indicates that the device is offline. + +--- + +### `device_removed` + +Indicates that the device has been removed. + +--- + +### `dormakaba_sites_disconnected` + +Indicates that one or more dormakaba sites associated with the connected account could not be connected. Contact dormakaba support. + +--- + +### `empty_backup_access_code_pool` + +Indicates that the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is empty. + +--- + +### `hub_disconnected` + +Indicates that the hub is disconnected. + +--- + +### `lockly_missing_wifi_bridge` + +Indicates that the Lockly lock is not connected to a Wi-Fi bridge. + +--- + +### `missing_device_credentials` + +Indicates that device credentials are missing. + +--- + ### `salto_ks_subscription_limit_exceeded` Indicates that the Salto site user limit has been reached. --- +### `subscription_required` + +Indicates that a subscription is required to connect. + +--- + +### `ttlock_lock_not_paired_to_gateway` + +Indicates that the lock is not paired with a gateway. + +--- + ## Warnings Each warning is an object with the following shape: ```json Example warning { - "warning_code": "ttlock_lock_gateway_unlocking_not_enabled", - "message": "Indicates that the Remote Unlock feature is not enabled in the settings.\"", + "warning_code": "partial_backup_access_code_pool", + "message": "Indicates that the backup access code is unhealthy.", "created_at": "2025-01-01T00:00:00.000Z" } ``` @@ -89,6 +165,14 @@ Each warning is an object with the following shape: Date and time at which Seam created the warning. + + Number of active access codes on the device when the warning was set. + + + + Maximum number of active access codes supported by the device. + + ### `accessory_keypad_setup_required` @@ -97,6 +181,18 @@ Indicates that the accessory keypad exists, but is not linked to the Igloohome B --- +### `device_communication_degraded` + +Indicates that the device appears to be unresponsive. + +--- + +### `device_has_flaky_connection` + +Indicates that the device has a flaky connection. + +--- + ### `hub_required_for_additional_capabilities` Indicates that a hub or relay must be connected to unlock additional capabilities such as remote unlock. @@ -115,20 +211,110 @@ Indicates that the key is in a locker that does not support the access codes API --- +### `lockly_time_zone_not_configured` + +Indicates that Seam detected that the Lockly device does not have a time zone configured. Time-bound codes may not work as expected. + +--- + +### `many_active_backup_codes` + +Indicates that there are too many backup codes. + +--- + +### `max_access_codes_reached` + +Indicates that the device has reached its maximum number of active access codes. Delete existing codes before creating new ones. + +--- + +### `partial_backup_access_code_pool` + +Indicates that the backup access code is unhealthy. + +--- + ### `power_saving_mode` Indicates that the device is in power saving mode and may have limited functionality. --- +### `provider_issue` + +Indicates a provider-specific issue that may affect device functionality. + +--- + +### `salto_ks_lock_access_code_support_removed` + +Indicates that a change in the reported device model has been detected for this Salto KS lock, which may occur after an IQ hub reset. Access code support may be affected. See https://help.getseam.com/articles/5098842588-salto-ks-lock-loses-access-code-support for troubleshooting steps. + +--- + +### `salto_ks_office_mode` + +Indicates that the Salto KS lock is in Office Mode. Access Codes will not unlock doors. + +--- + +### `salto_ks_privacy_mode` + +Indicates that the Salto KS lock is in Privacy Mode. Access Codes will not unlock doors. + +--- + +### `salto_ks_subscription_limit_almost_reached` + +Indicates that the Salto KS site has exceeded 80% of the maximum number of allowed users. Increase your subscription limit or delete some users from your site. + +--- + +### `scheduled_maintenance_window` + +Indicates that a scheduled maintenance window has been detected. + +--- + +### `third_party_integration_detected` + +Indicates that a third-party integration has been detected. + +--- + ### `ttlock_lock_gateway_unlocking_not_enabled` Indicates that the Remote Unlock feature is not enabled in the settings." --- +### `ttlock_weak_gateway_signal` + +Indicates that the gateway signal is weak. + +--- + +### `two_n_device_missing_timezone` + +Indicates that the 2N device does not have a time zone configured. Configure a time zone on the device to enable access codes. + +--- + +### `ultraloq_time_zone_unknown` + +Indicates that Seam does not know the time zone of the Ultraloq device. Set a time zone to enable time-bound access codes. + +--- + ### `unreliable_online_status` Indicates that the device may optimistically be reported as online because the provider does not reliably report its online status. --- + +### `wyze_device_missing_gateway` + +Indicates that the Wyze Lock is not connected to a gateway. + +--- diff --git a/mintlify-docs/api/thermostats/errors.mdx b/mintlify-docs/api/thermostats/errors.mdx index 6d6e9357f..4be1fce3c 100644 --- a/mintlify-docs/api/thermostats/errors.mdx +++ b/mintlify-docs/api/thermostats/errors.mdx @@ -9,9 +9,10 @@ Each error is an object with the following shape: ```json Example error { - "error_code": "auxiliary_heat_running", - "message": "Indicates that the auxiliary heat is running.", + "error_code": "account_disconnected", + "message": "Indicates that the account is disconnected.", "created_at": "2025-01-01T00:00:00.000Z", + "is_connected_account_error": true, "is_device_error": true } ``` @@ -32,26 +33,100 @@ Each error is an object with the following shape: Date and time at which Seam created the error. + + Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + + + + Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts/object) error. + + - Indicates that the error is a device error. + Indicates that the error is not a device error. +### `account_disconnected` + +Indicates that the account is disconnected. + +--- + ### `auxiliary_heat_running` Indicates that the auxiliary heat is running. --- +### `bridge_disconnected` + +Indicates that the Seam API cannot communicate with [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge), for example, if the Seam Bridge executable has stopped or if the computer running the Seam Bridge executable is offline. See also [Troubleshooting Your Access Control System](https://docs.seam.co/low-level-apis/access-systems/troubleshooting-your-access-control-system#acs_system.errors.seam_bridge_disconnected). + +--- + +### `device_disconnected` + +Indicates that the device is disconnected. + +--- + +### `device_offline` + +Indicates that the device is offline. + +--- + +### `device_removed` + +Indicates that the device has been removed. + +--- + +### `dormakaba_sites_disconnected` + +Indicates that one or more dormakaba sites associated with the connected account could not be connected. Contact dormakaba support. + +--- + +### `hub_disconnected` + +Indicates that the hub is disconnected. + +--- + +### `lockly_missing_wifi_bridge` + +Indicates that the Lockly lock is not connected to a Wi-Fi bridge. + +--- + +### `missing_device_credentials` + +Indicates that device credentials are missing. + +--- + +### `subscription_required` + +Indicates that a subscription is required to connect. + +--- + +### `ttlock_lock_not_paired_to_gateway` + +Indicates that the lock is not paired with a gateway. + +--- + ## Warnings Each warning is an object with the following shape: ```json Example warning { - "warning_code": "temperature_threshold_exceeded", - "message": "Indicates that the temperature threshold has been exceeded.", + "warning_code": "wyze_device_missing_gateway", + "message": "Indicates that the Wyze Lock is not connected to a gateway.", "created_at": "2025-01-01T00:00:00.000Z" } ``` @@ -74,8 +149,68 @@ Each warning is an object with the following shape: +### `device_communication_degraded` + +Indicates that the device appears to be unresponsive. + +--- + +### `device_has_flaky_connection` + +Indicates that the device has a flaky connection. + +--- + +### `lockly_time_zone_not_configured` + +Indicates that Seam detected that the Lockly device does not have a time zone configured. Time-bound codes may not work as expected. + +--- + +### `salto_ks_subscription_limit_almost_reached` + +Indicates that the Salto KS site has exceeded 80% of the maximum number of allowed users. Increase your subscription limit or delete some users from your site. + +--- + +### `scheduled_maintenance_window` + +Indicates that a scheduled maintenance window has been detected. + +--- + ### `temperature_threshold_exceeded` Indicates that the temperature threshold has been exceeded. --- + +### `third_party_integration_detected` + +Indicates that a third-party integration has been detected. + +--- + +### `ttlock_weak_gateway_signal` + +Indicates that the gateway signal is weak. + +--- + +### `two_n_device_missing_timezone` + +Indicates that the 2N device does not have a time zone configured. Configure a time zone on the device to enable access codes. + +--- + +### `ultraloq_time_zone_unknown` + +Indicates that Seam does not know the time zone of the Ultraloq device. Set a time zone to enable time-bound access codes. + +--- + +### `wyze_device_missing_gateway` + +Indicates that the Wyze Lock is not connected to a gateway. + +---