docs: README and API updates about APIs

This commit is contained in:
gak 2023-10-25 15:49:57 +11:00
parent 514e8ec134
commit 463355ce2d
No known key found for this signature in database
3 changed files with 231 additions and 241 deletions

444
API.md
View file

@ -1,249 +1,237 @@
# Tesla API Matrix
List of all known Tesla APIs, and if this crate supports it, and which of the Tesla enpoints support them.
List of all known Tesla APIs (official and unofficial) and if [Teslatte](https://github.com/gak/teslatte) supports it. Some of the Owners API endpoints aren't listed here because they are not currently planned.
As of right now, I am not able to test the two official APIs because my request to use the APIs were rejected.
## APIs
* **Owners API:** A HTTP/REST API (`owner-api.teslamotors.com`) that is used by the Tesla mobile app. It is unofficial and not supported by Tesla. It can access and send commands to vehicles and energy products. The API is reverse engineered by the community, where the Owners API listed below is from the [timdorr/tesla-api](https://github.com/timdorr/tesla-api) repository. Specifically it scrapes the [ownerapi_endpoints.json](https://github.com/timdorr/tesla-api/blob/master/ownerapi_endpoints.json).
* **Fleet API:** Tesla's official HTTP/REST API (released late 2023). The API details are scraped from the [Fleet API documentation](https://developer.tesla.com/docs/fleet-api). The URI paths look identical to the Owners API, except for the domains used, and that it is only for vehicles.
* **Command Mode:** Tesla's official SDK for sending commands to the vehicle (released late 2023). This interface uses Protocol Buffers. There is a [Go repository for the SDK](https://github.com/teslamotors/vehicle-command/) which also includes a proxy for the Fleet API. Unfortunately they have not yet released the protobuf sources, just generated code.
## Notes
This list is automatically generated by the `tesla_api_coverage` tool in this repository. It still needs cleaning up. Some of these items listed might be duplicates, wrong, the API is unknown, or won't be implemented. Please create an issue if you spot any errors.
## API Coverage
### Legend
- ✅ Supported by Teslatte
- 🔴 API exists but not supported by Teslatte
- Blank - API doesn't exist
- ✅ Supported by Teslatte.
- 🔴 API exists but not supported by Teslatte.
- API doesn't exist.
Currently only the Owner API is partially supported by this crate.
<!-- tesla_api_coverage start table -->
| API | Owners API (timdorr) | Fleet API | Command Mode |
| API | Owners API | Fleet API | Command Mode |
| --- | --- | --- | --- |
| actuate-trunk | 🔴 | 🔴 | |
| add-key | 🔴 | | 🔴 |
| add-key-request | | | 🔴 |
| add-managed-charging-site | 🔴 | | |
| adjust-volume | 🔴 | 🔴 | |
| app-feedback-entitlements | 🔴 | | |
| app-feedback-logs | 🔴 | | |
| app-feedback-metadata | 🔴 | | |
| actuate-trunk | 🔴 | 🔴 | |
| add-key | 🔴 | | 🔴 |
| add-key-request | | | 🔴 |
| add-managed-charging-site | 🔴 | | |
| adjust-volume | 🔴 | 🔴 | |
| app-feedback-entitlements | 🔴 | | |
| app-feedback-logs | 🔴 | | |
| app-feedback-metadata | 🔴 | | |
| auto-conditioning-start | ✅ | 🔴 | 🔴 |
| auto-conditioning-stop | ✅ | 🔴 | |
| autosecure-modelx | | | 🔴 |
| backup-key | | 🔴 | |
| backup-reserve | 🔴 | | |
| calendar-history-data | 🔴 | | |
| calendar-sync | 🔴 | | |
| auto-conditioning-stop | ✅ | 🔴 | |
| autosecure-modelx | | | 🔴 |
| backup-key | | 🔴 | |
| backup-reserve | 🔴 | | |
| calendar-history-data | 🔴 | | |
| calendar-sync | 🔴 | | |
| cancel-software-update | 🔴 | 🔴 | 🔴 |
| charge-max-range | ✅ | 🔴 | |
| charge-max-range | ✅ | 🔴 | |
| charge-port-door-close | ✅ | 🔴 | 🔴 |
| charge-port-door-open | ✅ | 🔴 | 🔴 |
| charge-standard | ✅ | 🔴 | |
| charge-standard | ✅ | 🔴 | |
| charge-start | ✅ | 🔴 | 🔴 |
| charge-stop | ✅ | 🔴 | 🔴 |
| charging-sessions-only-for-business-fleet-owners | | 🔴 | |
| check-energy-product-registration | 🔴 | | |
| climate-off | | | 🔴 |
| climate-on | | | 🔴 |
| climate-set-temp | | | 🔴 |
| create-energy-site-share-invite | 🔴 | | |
| dashcam-save-clip | 🔴 | | |
| deactivate-device-token | 🔴 | | |
| door-lock | ✅ | 🔴 | |
| door-unlock | ✅ | 🔴 | |
| drive | | | 🔴 |
| driving-plan | 🔴 | | |
| energy-event | 🔴 | | |
| energy-register-product | 🔴 | | |
| energy-site-backup-time-remaining | 🔴 | | |
| energy-site-command | 🔴 | | |
| energy-site-enroll-program | 🔴 | | |
| energy-site-import-export-config | 🔴 | | |
| energy-site-onboarding-tips | 🔴 | | |
| energy-site-opt-event | 🔴 | | |
| energy-site-preference | 🔴 | | |
| energy-site-program-details | 🔴 | | |
| energy-site-programs | 🔴 | | |
| energy-site-telemetry-history | 🔴 | | |
| energy-site-user-settings | 🔴 | | |
| energy-sites-calendar-history | ✅ | | |
| energy-sites-live-status | ✅ | | |
| energy-sites-site-info | ✅ | | |
| energy-sites-site-status | ✅ | | |
| energy-wall-connector-firmware-download-url | 🔴 | | |
| erase-user-data | | 🔴 | |
| feature-config | 🔴 | 🔴 | |
| fetch-energy-site-share-invites | 🔴 | | |
| fetch-energy-site-shared-users | 🔴 | | |
| fetch-vehicle-shared-drivers | 🔴 | | |
| charging-sessions-only-for-business-fleet-owners | | 🔴 | |
| check-energy-product-registration | 🔴 | | |
| climate-off | | | 🔴 |
| climate-on | | | 🔴 |
| climate-set-temp | | | 🔴 |
| create-energy-site-share-invite | 🔴 | | |
| dashcam-save-clip | 🔴 | | |
| deactivate-device-token | 🔴 | | |
| door-lock | ✅ | 🔴 | |
| door-unlock | ✅ | 🔴 | |
| drive | | | 🔴 |
| driving-plan | 🔴 | | |
| energy-event | 🔴 | | |
| energy-register-product | 🔴 | | |
| energy-site-backup-time-remaining | 🔴 | | |
| energy-site-command | 🔴 | | |
| energy-site-enroll-program | 🔴 | | |
| energy-site-import-export-config | 🔴 | | |
| energy-site-onboarding-tips | 🔴 | | |
| energy-site-opt-event | 🔴 | | |
| energy-site-preference | 🔴 | | |
| energy-site-program-details | 🔴 | | |
| energy-site-programs | 🔴 | | |
| energy-site-telemetry-history | 🔴 | | |
| energy-site-user-settings | 🔴 | | |
| energy-sites-calendar-history | ✅ | | |
| energy-sites-live-status | ✅ | | |
| energy-sites-site-info | ✅ | | |
| energy-sites-site-status | ✅ | | |
| energy-wall-connector-firmware-download-url | 🔴 | | |
| erase-user-data | | 🔴 | |
| feature-config | 🔴 | 🔴 | |
| fetch-energy-site-share-invites | 🔴 | | |
| fetch-energy-site-shared-users | 🔴 | | |
| fetch-vehicle-shared-drivers | 🔴 | | |
| flash-lights | ✅ | 🔴 | 🔴 |
| frunk-open | | | 🔴 |
| get | | | 🔴 |
| get-charge-on-solar-feature | 🔴 | | |
| get-managed-charging-sites | 🔴 | | |
| get-ownership-xp-config | 🔴 | | |
| get-upcoming-service-visit-data | 🔴 | | |
| guest-mode | | 🔴 | |
| hermes-authorization | 🔴 | | |
| hermes-vehicle-authorization | 🔴 | | |
| frunk-open | | | 🔴 |
| get | | | 🔴 |
| get-charge-on-solar-feature | 🔴 | | |
| get-managed-charging-sites | 🔴 | | |
| get-ownership-xp-config | 🔴 | | |
| get-upcoming-service-visit-data | 🔴 | | |
| guest-mode | | 🔴 | |
| hermes-authorization | 🔴 | | |
| hermes-vehicle-authorization | 🔴 | | |
| honk-horn | ✅ | 🔴 | 🔴 |
| list-keys | | | 🔴 |
| lock | | | 🔴 |
| mattermost | 🔴 | | |
| me | 🔴 | 🔴 | |
| media-next-fav | 🔴 | 🔴 | |
| media-next-track | 🔴 | 🔴 | |
| media-prev-fav | 🔴 | 🔴 | |
| media-prev-track | 🔴 | 🔴 | |
| media-set-volume | | | 🔴 |
| media-toggle-playback | 🔴 | 🔴 | |
| media-volume-down | 🔴 | 🔴 | |
| media-volume-up | 🔴 | | |
| message-center-message | 🔴 | | |
| message-center-message-action-update | 🔴 | | |
| message-center-message-count | 🔴 | | |
| message-center-message-list | 🔴 | | |
| mobile-enabled | | 🔴 | |
| navigation-gps-request | 🔴 | 🔴 | |
| navigation-request | | 🔴 | |
| navigation-route | 🔴 | | |
| navigation-sc-request | 🔴 | 🔴 | |
| nearby-charging-sites | 🔴 | 🔴 | |
| off-grid-vehicle-charging-reserve | 🔴 | | |
| onboarding-experience | 🔴 | | |
| onboarding-experience-page | 🔴 | | |
| operation-mode | 🔴 | | |
| orders | 🔴 | 🔴 | |
| ping | | | 🔴 |
| place-suggestions | 🔴 | | |
| plan-trip | 🔴 | | |
| post | | | 🔴 |
| powerwall-energy-history | ✅ | | |
| powerwall-order-page | 🔴 | | |
| powerwall-order-session-data | 🔴 | | |
| powerwall-status | ✅ | | |
| product-info | | | 🔴 |
| products | ✅ | | |
| public-key | | 🔴 | |
| rate-tariffs | 🔴 | | |
| redeem-energy-site-share-invite | 🔴 | | |
| redeem-vehicle-share-invite | 🔴 | | |
| referral-data | 🔴 | | |
| referral-page | 🔴 | | |
| region | | 🔴 | |
| register | | 🔴 | |
| release-notes | 🔴 | | |
| remote-auto-seat-climate-request | 🔴 | 🔴 | |
| remote-auto-steering-wheel-heat-climate-request | 🔴 | 🔴 | |
| remote-boombox | 🔴 | 🔴 | |
| remote-seat-cooler-request | 🔴 | 🔴 | |
| remote-seat-heater-request | 🔴 | 🔴 | |
| remote-start-drive | ✅ | 🔴 | |
| remote-steering-wheel-heat-level-request | 🔴 | 🔴 | |
| remote-steering-wheel-heater-request | 🔴 | 🔴 | |
| remove-energy-site-share-user | 🔴 | | |
| remove-key | | | 🔴 |
| remove-managed-charging-site | 🔴 | | |
| remove-vehicle-share-driver | 🔴 | | |
| rename-key | | | 🔴 |
| reset-pin-to-drive-pin | | 🔴 | |
| reset-valet-pin | 🔴 | 🔴 | |
| retrieve-notification-preferences | 🔴 | | |
| reverse-geocoding | 🔴 | | |
| revoke-energy-site-share-invite | 🔴 | | |
| revoke-vehicle-share-invite | 🔴 | | |
| roadside-assistance-data | 🔴 | | |
| roadside-assistance-page | 🔴 | | |
| schedule-software-update | 🔴 | 🔴 | |
| seat-heater | | | 🔴 |
| send-device-key | 🔴 | | |
| send-notification-confirmation | 🔴 | | |
| send-notification-preferences | 🔴 | | |
| send-to-vehicle | 🔴 | | |
| sentry-mode | | | 🔴 |
| service-data | 🔴 | 🔴 | |
| session-info | | | 🔴 |
| set-bioweapon-mode | 🔴 | 🔴 | |
| set-cabin-overheat-protection | 🔴 | 🔴 | |
| list-keys | | | 🔴 |
| lock | | | 🔴 |
| mattermost | 🔴 | | |
| me | 🔴 | 🔴 | |
| media-next-fav | 🔴 | 🔴 | |
| media-next-track | 🔴 | 🔴 | |
| media-prev-fav | 🔴 | 🔴 | |
| media-prev-track | 🔴 | 🔴 | |
| media-set-volume | | | 🔴 |
| media-toggle-playback | 🔴 | 🔴 | |
| media-volume-down | 🔴 | 🔴 | |
| media-volume-up | 🔴 | | |
| message-center-message | 🔴 | | |
| message-center-message-action-update | 🔴 | | |
| message-center-message-count | 🔴 | | |
| message-center-message-list | 🔴 | | |
| mobile-enabled | | 🔴 | |
| navigation-gps-request | 🔴 | 🔴 | |
| navigation-request | | 🔴 | |
| navigation-route | 🔴 | | |
| navigation-sc-request | 🔴 | 🔴 | |
| nearby-charging-sites | 🔴 | 🔴 | |
| off-grid-vehicle-charging-reserve | 🔴 | | |
| onboarding-experience | 🔴 | | |
| onboarding-experience-page | 🔴 | | |
| operation-mode | 🔴 | | |
| orders | 🔴 | 🔴 | |
| ping | | | 🔴 |
| place-suggestions | 🔴 | | |
| plan-trip | 🔴 | | |
| post | | | 🔴 |
| powerwall-energy-history | ✅ | | |
| powerwall-order-page | 🔴 | | |
| powerwall-order-session-data | 🔴 | | |
| powerwall-status | ✅ | | |
| product-info | | | 🔴 |
| products | ✅ | | |
| public-key | | 🔴 | |
| rate-tariffs | 🔴 | | |
| redeem-energy-site-share-invite | 🔴 | | |
| redeem-vehicle-share-invite | 🔴 | | |
| referral-data | 🔴 | | |
| referral-page | 🔴 | | |
| region | | 🔴 | |
| register | | 🔴 | |
| release-notes | 🔴 | | |
| remote-auto-seat-climate-request | 🔴 | 🔴 | |
| remote-auto-steering-wheel-heat-climate-request | 🔴 | 🔴 | |
| remote-boombox | 🔴 | 🔴 | |
| remote-seat-cooler-request | 🔴 | 🔴 | |
| remote-seat-heater-request | 🔴 | 🔴 | |
| remote-start-drive | ✅ | 🔴 | |
| remote-steering-wheel-heat-level-request | 🔴 | 🔴 | |
| remote-steering-wheel-heater-request | 🔴 | 🔴 | |
| remove-energy-site-share-user | 🔴 | | |
| remove-key | | | 🔴 |
| remove-managed-charging-site | 🔴 | | |
| remove-vehicle-share-driver | 🔴 | | |
| rename-key | | | 🔴 |
| reset-pin-to-drive-pin | | 🔴 | |
| reset-valet-pin | 🔴 | 🔴 | |
| retrieve-notification-preferences | 🔴 | | |
| reverse-geocoding | 🔴 | | |
| revoke-energy-site-share-invite | 🔴 | | |
| revoke-vehicle-share-invite | 🔴 | | |
| roadside-assistance-data | 🔴 | | |
| roadside-assistance-page | 🔴 | | |
| schedule-software-update | 🔴 | 🔴 | |
| seat-heater | | | 🔴 |
| send-device-key | 🔴 | | |
| send-notification-confirmation | 🔴 | | |
| send-notification-preferences | 🔴 | | |
| send-to-vehicle | 🔴 | | |
| sentry-mode | | | 🔴 |
| service-data | 🔴 | 🔴 | |
| session-info | | | 🔴 |
| set-bioweapon-mode | 🔴 | 🔴 | |
| set-cabin-overheat-protection | 🔴 | 🔴 | |
| set-charge-limit | ✅ | 🔴 | 🔴 |
| set-charging-amps | ✅ | 🔴 | |
| set-climate-keeper-mode | 🔴 | 🔴 | |
| set-cop-temp | 🔴 | 🔴 | |
| set-managed-charge-current-request | | 🔴 | |
| set-managed-charger-location | | 🔴 | |
| set-managed-scheduled-charging-time | | 🔴 | |
| set-pin-to-drive | | 🔴 | |
| set-preconditioning-max | 🔴 | 🔴 | |
| set-scheduled-charging | ✅ | 🔴 | |
| set-scheduled-departure | ✅ | 🔴 | |
| set-sentry-mode | 🔴 | 🔴 | |
| set-temps | ✅ | 🔴 | |
| set-valet-mode | 🔴 | 🔴 | |
| set-vehicle-name | | 🔴 | |
| share-invites | 🔴 | 🔴 | |
| share-invites-create | 🔴 | 🔴 | |
| share-invites-revoke | | 🔴 | |
| site-address | 🔴 | | |
| site-config | 🔴 | | |
| site-data | 🔴 | | |
| site-name | 🔴 | | |
| site-tariff | 🔴 | | |
| software-update-start | | | 🔴 |
| solar-savings-forecast | 🔴 | | |
| speed-limit-activate | 🔴 | 🔴 | |
| speed-limit-clear-pin | 🔴 | 🔴 | |
| speed-limit-deactivate | 🔴 | 🔴 | |
| speed-limit-set-limit | 🔴 | 🔴 | |
| splunk-telemetry | 🔴 | | |
| static-charger-file | 🔴 | | |
| static-supercharger-file | 🔴 | | |
| status | 🔴 | | |
| steering-wheel-heater | | | 🔴 |
| storm-mode-settings | 🔴 | | |
| sun-roof-control | 🔴 | 🔴 | |
| take-drivenote | 🔴 | 🔴 | |
| time-of-use-settings | 🔴 | | |
| trigger-homelink | 🔴 | 🔴 | |
| trigger-vehicle-screenshot | 🔴 | | |
| trunk-close | | | 🔴 |
| trunk-move | | | 🔴 |
| trunk-open | | | 🔴 |
| unlock | | | 🔴 |
| update-calendar-entries | | 🔴 | |
| update-charge-on-solar-feature | 🔴 | | |
| user-account-upload-profile-picture | 🔴 | | |
| user-reset-vault | 🔴 | | |
| vehicle | | 🔴 | |
| vehicle-charge-history | 🔴 | | |
| vehicle-data | ✅ | 🔴 | |
| vehicle-download-vault | 🔴 | | |
| vehicle-energy-sites | 🔴 | | |
| vehicle-subscriptions | | 🔴 | |
| vehicle-subscriptions-set | | 🔴 | |
| vehicle-summary | 🔴 | | |
| vehicle-upload-vault | 🔴 | | |
| vehicles | ✅ | 🔴 | |
| set-charging-amps | ✅ | 🔴 | |
| set-climate-keeper-mode | 🔴 | 🔴 | |
| set-cop-temp | 🔴 | 🔴 | |
| set-managed-charge-current-request | | 🔴 | |
| set-managed-charger-location | | 🔴 | |
| set-managed-scheduled-charging-time | | 🔴 | |
| set-pin-to-drive | | 🔴 | |
| set-preconditioning-max | 🔴 | 🔴 | |
| set-scheduled-charging | ✅ | 🔴 | |
| set-scheduled-departure | ✅ | 🔴 | |
| set-sentry-mode | 🔴 | 🔴 | |
| set-temps | ✅ | 🔴 | |
| set-valet-mode | 🔴 | 🔴 | |
| set-vehicle-name | | 🔴 | |
| share-invites | 🔴 | 🔴 | |
| share-invites-create | 🔴 | 🔴 | |
| share-invites-revoke | | 🔴 | |
| site-address | 🔴 | | |
| site-config | 🔴 | | |
| site-data | 🔴 | | |
| site-name | 🔴 | | |
| site-tariff | 🔴 | | |
| software-update-start | | | 🔴 |
| solar-savings-forecast | 🔴 | | |
| speed-limit-activate | 🔴 | 🔴 | |
| speed-limit-clear-pin | 🔴 | 🔴 | |
| speed-limit-deactivate | 🔴 | 🔴 | |
| speed-limit-set-limit | 🔴 | 🔴 | |
| splunk-telemetry | 🔴 | | |
| static-charger-file | 🔴 | | |
| static-supercharger-file | 🔴 | | |
| status | 🔴 | | |
| steering-wheel-heater | | | 🔴 |
| storm-mode-settings | 🔴 | | |
| sun-roof-control | 🔴 | 🔴 | |
| take-drivenote | 🔴 | 🔴 | |
| time-of-use-settings | 🔴 | | |
| trigger-homelink | 🔴 | 🔴 | |
| trigger-vehicle-screenshot | 🔴 | | |
| trunk-close | | | 🔴 |
| trunk-move | | | 🔴 |
| trunk-open | | | 🔴 |
| unlock | | | 🔴 |
| update-calendar-entries | | 🔴 | |
| update-charge-on-solar-feature | 🔴 | | |
| user-account-upload-profile-picture | 🔴 | | |
| user-reset-vault | 🔴 | | |
| vehicle | | 🔴 | |
| vehicle-charge-history | 🔴 | | |
| vehicle-data | ✅ | 🔴 | |
| vehicle-download-vault | 🔴 | | |
| vehicle-energy-sites | 🔴 | | |
| vehicle-subscriptions | | 🔴 | |
| vehicle-subscriptions-set | | 🔴 | |
| vehicle-summary | 🔴 | | |
| vehicle-upload-vault | 🔴 | | |
| vehicles | ✅ | 🔴 | |
| wake-up | ✅ | 🔴 | 🔴 |
| warranty-details | | 🔴 | |
| window-control | 🔴 | 🔴 | |
| warranty-details | | 🔴 | |
| window-control | 🔴 | 🔴 | |
<!-- tesla_api_coverage end table -->

View file

@ -13,17 +13,23 @@
Teslatte is both a CLI and a Rust crate for interacting with the Tesla API.
A Tesla API using the `owner-api.teslamotors.com` endpoint as well as parts of the OAuth flow.
It is fairly trivial to add in new endpoints if you feel like creating a PR, but please:
* Write some unit tests if it relies on new or changed data structures.
* Let me know if your PR is a massive change before spending a lot of time on it.
Currently, it only supports some the API.
## APIs implemented
It is fairly trivial to add in new endpoints if you feel like creating a PR. Please let me know if your PR is a massive change before spending a lot of time on it.
Only the Owner API is partially supported by this crate, with plans to support the Fleet API and the Command Mode SDK.
[`API.md`](API.md) contains a table of all known Tesla APIs (official and unofficial) and if Teslatte supports it, and where the API details were scraped from.
## References
Thanks to https://tesla-api.timdorr.com/ for their excellent reference.
Tesla API recently released [API documentation for their "Fleet API"](https://developer.tesla.com/docs/fleet-api) which appears to be similar to the Tesla Owner API.
Tesla recently released [API documentation for their "Fleet API"](https://developer.tesla.com/docs/fleet-api) which appears to be similar to the Tesla Owner API.
Tesla also recently released a [Command Mode SDK](https://github.com/teslamotors/vehicle-command/) which also includes a proxy for the Fleet API.
## CLI

View file

@ -1,7 +1,3 @@
use crate::fleet::FleetEndpoint;
use crate::teslatte::TeslatteEndpoint;
use crate::timdorr::TimdorrEndpoint;
use crate::vehicle_command::VehicleCommandEndpoint;
use crate::Endpoint;
use std::collections::HashMap;
use std::fs::File;
@ -29,7 +25,7 @@ pub fn generate(merged: &HashMap<String, Endpoint>) -> anyhow::Result<()> {
let after = parts.next().unwrap();
let mut table = vec![];
table.push("| API | Owners API (timdorr) | Fleet API | Command Mode |".to_string());
table.push("| API | Owners API | Fleet API | Command Mode |".to_string());
table.push("| --- | --- | --- | --- |".to_string());
// Sort by URL for into a Vec for now. Keep the key.
@ -45,19 +41,19 @@ pub fn generate(merged: &HashMap<String, Endpoint>) -> anyhow::Result<()> {
} else if endpoint.timdorr.is_some() {
row.push("🔴".to_string());
} else {
row.push("".to_string());
row.push("".to_string());
}
if endpoint.fleet.is_some() {
row.push("🔴".to_string());
} else {
row.push("".to_string());
row.push("".to_string());
}
if endpoint.vehicle_command.is_some() {
row.push("🔴".to_string());
} else {
row.push("".to_string());
row.push("".to_string());
}
table.push(format!("| {} |", row.join(" | ")));
@ -66,7 +62,7 @@ pub fn generate(merged: &HashMap<String, Endpoint>) -> anyhow::Result<()> {
let table = table.join("\n");
// Now join them together and write the whole file back.
let contents = format!("{before}\n{START}\n{table}\n{END}\n{after}");
let contents = format!("{before}{START}\n{table}\n{END}{after}");
let mut file = File::create(&path)?;
file.write_all(contents.as_bytes())?;