{"owner":"lissy93","repo":"dashy","hasSpec":true,"specFile":"services/endpoints/api/openapi.yml","branch":"HEAD","format":"yaml","version":"3.x (YAML)","title":"dashy","description":"","endpoints":[],"spec":"openapi: 3.1.0\n\ninfo:\n  title: Dashy REST API\n  version: 1.0.0\n  summary: Read and write your Dashy config over HTTP.\n  description: |\n      The Dashy API is a simple opt-in REST API for CRUD actions over your config.\n      For full usage guide, examples and architecture, see the [API Docs](https://dashy.to/docs/api).\n\n      <details>\n      <summary>Enabling the API</summary>\n\n      The API is off by default. To enable it, set the `ENABLE_API` env var to true,\n      and restart Dashy.\n\n      </details>\n\n      <details>\n      <summary>Authenticating</summary>\n\n      By default, the API will use the same auth system as the rest of your Dashy instance.\n      If you don't have auth configured, or would prefer to use bearer token for the API instead,\n      then you can do so, by setting an `API_TOKEN` environmental variable,\n      and then passing that as a bearer token in the authorization header when making requests,\n      like `curl -H 'Authorization: Bearer <your-long-random-secret>' http://dashy.local/api/config`\n\n      </details>\n\n      > [!IMPORTANT]\n      > There is no warranty. It is your responsibility to correctly configure and protect your instance.\n      > The API is experimental.\n\n  contact:\n    name: GitHub\n    url: https://github.com/Lissy93/dashy\n  license:\n    name: MIT\n    url: https://github.com/Lissy93/dashy/blob/master/LICENSE\n  x-logo:\n    url: https://raw.githubusercontent.com/Lissy93/dashy/master/public/web-icons/dashy-logo.png\n    altText: Dashy\n    href: https://dashy.to\n\nexternalDocs:\n  description: API guide\n  url: https://dashy.to/docs/api\n\nservers:\n  - url: \"{scheme}://{host}/api\"\n    description: Your Dashy instance\n    variables:\n      scheme:\n        enum: [http, https]\n        default: http\n      host:\n        default: localhost:4000\n\ntags:\n  - name: Files\n    description: List, read and replace whole config files.\n  - name: Keys\n    description: Read and replace a single top-level config key.\n  - name: Sections\n    description: Create, read, update and delete sections.\n  - name: Items\n    description: Create, read, update and delete items within a section.\n\nsecurity:\n  - bearerAuth: []\n  - basicAuth: []\n  - {}\n\npaths:\n  /config:\n    get:\n      tags: [Files]\n      summary: List config files\n      operationId: listConfigFiles\n      description: Lists every YAML config file in the user-data directory.\n      responses:\n        \"200\":\n          description: Config files\n          content:\n            application/json:\n              schema: { $ref: \"#/components/schemas/FileList\" }\n              example: { success: true, files: [conf.yml, home-lab.yml] }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"404\": { $ref: \"#/components/responses/Disabled\" }\n\n  /config/{filename}:\n    parameters:\n      - $ref: \"#/components/parameters/Filename\"\n    get:\n      tags: [Files]\n      summary: Get config file\n      operationId: getConfigFile\n      description: Returns the parsed config file as JSON.\n      responses:\n        \"200\":\n          description: Parsed config\n          content:\n            application/json:\n              schema: { $ref: \"#/components/schemas/Config\" }\n        \"400\": { $ref: \"#/components/responses/BadRequest\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"404\": { $ref: \"#/components/responses/NotFound\" }\n        \"500\": { $ref: \"#/components/responses/ParseError\" }\n    put:\n      tags: [Files]\n      summary: Replace config file\n      operationId: replaceConfigFile\n      description: |\n        Overwrites the whole file. Writes to `conf.yml` are validated against the\n        config schema; sub-pages are not. A timestamped backup is taken first.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema: { $ref: \"#/components/schemas/Config\" }\n            example: { pageInfo: { title: My Dashboard }, sections: [] }\n      responses:\n        \"200\": { $ref: \"#/components/responses/Saved\" }\n        \"400\": { $ref: \"#/components/responses/BadRequest\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"403\": { $ref: \"#/components/responses/Forbidden\" }\n        \"413\": { $ref: \"#/components/responses/TooLarge\" }\n\n  /config/{filename}/{key}:\n    parameters:\n      - $ref: \"#/components/parameters/Filename\"\n      - $ref: \"#/components/parameters/Key\"\n    get:\n      tags: [Keys]\n      summary: Get top-level key\n      operationId: getConfigKey\n      description: Returns a single top-level key from the config.\n      responses:\n        \"200\":\n          description: Key value\n          content:\n            application/json:\n              schema: { $ref: \"#/components/schemas/KeyValue\" }\n        \"400\": { $ref: \"#/components/responses/BadRequest\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"404\": { $ref: \"#/components/responses/NotFound\" }\n    put:\n      tags: [Keys]\n      summary: Replace top-level key\n      operationId: replaceConfigKey\n      description: Replaces a single top-level key, leaving the rest of the file untouched.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema: { $ref: \"#/components/schemas/KeyValue\" }\n            example: { theme: nord-frost, layout: auto }\n      responses:\n        \"200\": { $ref: \"#/components/responses/Saved\" }\n        \"400\": { $ref: \"#/components/responses/BadRequest\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"403\": { $ref: \"#/components/responses/Forbidden\" }\n        \"413\": { $ref: \"#/components/responses/TooLarge\" }\n\n  /config/{filename}/sections:\n    parameters:\n      - $ref: \"#/components/parameters/Filename\"\n    post:\n      tags: [Sections]\n      summary: Add section\n      operationId: addSection\n      description: Appends a new section. A `name` is required.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema: { $ref: \"#/components/schemas/Section\" }\n            example: { name: Monitoring, icon: fas fa-chart-line, items: [] }\n      responses:\n        \"201\":\n          description: Section added\n          content:\n            application/json:\n              schema: { $ref: \"#/components/schemas/SectionResult\" }\n        \"400\": { $ref: \"#/components/responses/BadRequest\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"403\": { $ref: \"#/components/responses/Forbidden\" }\n\n  /config/{filename}/sections/{sid}:\n    parameters:\n      - $ref: \"#/components/parameters/Filename\"\n      - $ref: \"#/components/parameters/SectionId\"\n    get:\n      tags: [Sections]\n      summary: Get section\n      operationId: getSection\n      responses:\n        \"200\":\n          description: Section\n          content:\n            application/json:\n              schema: { $ref: \"#/components/schemas/Section\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"404\": { $ref: \"#/components/responses/NotFound\" }\n    patch:\n      tags: [Sections]\n      summary: Update section\n      operationId: updateSection\n      description: Shallow-merges the supplied fields. Sending `items` replaces the whole array.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema: { $ref: \"#/components/schemas/SectionFields\" }\n            example: { name: Renamed Section }\n      responses:\n        \"200\":\n          description: Updated section\n          content:\n            application/json:\n              schema:\n                allOf:\n                  - $ref: \"#/components/schemas/WriteResult\"\n                  - type: object\n                    properties:\n                      section: { $ref: \"#/components/schemas/Section\" }\n        \"400\": { $ref: \"#/components/responses/BadRequest\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"403\": { $ref: \"#/components/responses/Forbidden\" }\n        \"404\": { $ref: \"#/components/responses/NotFound\" }\n    delete:\n      tags: [Sections]\n      summary: Delete section\n      operationId: deleteSection\n      responses:\n        \"200\": { $ref: \"#/components/responses/Saved\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"403\": { $ref: \"#/components/responses/Forbidden\" }\n        \"404\": { $ref: \"#/components/responses/NotFound\" }\n\n  /config/{filename}/sections/{sid}/items:\n    parameters:\n      - $ref: \"#/components/parameters/Filename\"\n      - $ref: \"#/components/parameters/SectionId\"\n    get:\n      tags: [Items]\n      summary: List items\n      operationId: listItems\n      responses:\n        \"200\":\n          description: Items in the section\n          content:\n            application/json:\n              schema:\n                type: array\n                items: { $ref: \"#/components/schemas/Item\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"404\": { $ref: \"#/components/responses/NotFound\" }\n    post:\n      tags: [Items]\n      summary: Add item\n      operationId: addItem\n      description: Appends a new item to the section. A `title` is required.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema: { $ref: \"#/components/schemas/Item\" }\n            example: { title: Grafana, url: https://grafana.local, icon: hl-grafana }\n      responses:\n        \"201\":\n          description: Item added\n          content:\n            application/json:\n              schema: { $ref: \"#/components/schemas/ItemResult\" }\n        \"400\": { $ref: \"#/components/responses/BadRequest\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"403\": { $ref: \"#/components/responses/Forbidden\" }\n        \"404\": { $ref: \"#/components/responses/NotFound\" }\n\n  /config/{filename}/sections/{sid}/items/{iid}:\n    parameters:\n      - $ref: \"#/components/parameters/Filename\"\n      - $ref: \"#/components/parameters/SectionId\"\n      - $ref: \"#/components/parameters/ItemId\"\n    get:\n      tags: [Items]\n      summary: Get item\n      operationId: getItem\n      responses:\n        \"200\":\n          description: Item\n          content:\n            application/json:\n              schema: { $ref: \"#/components/schemas/Item\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"404\": { $ref: \"#/components/responses/NotFound\" }\n    patch:\n      tags: [Items]\n      summary: Update item\n      operationId: updateItem\n      description: Shallow-merges the supplied fields onto the item.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema: { $ref: \"#/components/schemas/ItemFields\" }\n            example: { url: https://grafana.example.com }\n      responses:\n        \"200\":\n          description: Updated item\n          content:\n            application/json:\n              schema:\n                allOf:\n                  - $ref: \"#/components/schemas/WriteResult\"\n                  - type: object\n                    properties:\n                      item: { $ref: \"#/components/schemas/Item\" }\n        \"400\": { $ref: \"#/components/responses/BadRequest\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"403\": { $ref: \"#/components/responses/Forbidden\" }\n        \"404\": { $ref: \"#/components/responses/NotFound\" }\n    delete:\n      tags: [Items]\n      summary: Delete item\n      operationId: deleteItem\n      responses:\n        \"200\": { $ref: \"#/components/responses/Saved\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"403\": { $ref: \"#/components/responses/Forbidden\" }\n        \"404\": { $ref: \"#/components/responses/NotFound\" }\n\ncomponents:\n  securitySchemes:\n    bearerAuth:\n      type: http\n      scheme: bearer\n      description: |\n        Send `Authorization: Bearer <token>`. Use either an `API_TOKEN` (grants full\n        admin access) or, with OIDC/Keycloak, your ID token.\n    basicAuth:\n      type: http\n      scheme: basic\n      description: Dashy's HTTP basic auth (`ENABLE_HTTP_AUTH` users or `BASIC_AUTH_USERNAME`/`PASSWORD`).\n\n  parameters:\n    Filename:\n      name: filename\n      in: path\n      required: true\n      description: A YAML config file in your user-data directory.\n      schema: { type: string, pattern: \"^[^/\\\\\\\\]+\\\\.ya?ml$\" }\n      example: conf.yml\n    Key:\n      name: key\n      in: path\n      required: true\n      description: A top-level config key.\n      schema: { type: string, enum: [pageInfo, appConfig, sections, pages] }\n      example: appConfig\n    SectionId:\n      name: sid\n      in: path\n      required: true\n      description: Section index (zero-based) or exact `name` (URL-encoded).\n      schema: { type: string }\n      example: \"0\"\n    ItemId:\n      name: iid\n      in: path\n      required: true\n      description: Item index (zero-based) or exact `title` (URL-encoded).\n      schema: { type: string }\n      example: \"0\"\n\n  responses:\n    Saved:\n      description: Saved\n      content:\n        application/json:\n          schema: { $ref: \"#/components/schemas/WriteResult\" }\n          example: { success: true, message: Config has been written to disk }\n    Disabled:\n      description: API not enabled\n      content:\n        application/json:\n          schema: { $ref: \"#/components/schemas/Error\" }\n          example: { success: false, message: \"API not enabled. Set ENABLE_API=true to use the REST API.\" }\n    BadRequest:\n      description: Invalid filename, key, body or schema\n      content:\n        application/json:\n          schema: { $ref: \"#/components/schemas/Error\" }\n          example: { success: false, message: \"Section must have a 'name'\" }\n    Unauthorized:\n      description: Authentication required or invalid\n      content:\n        application/json:\n          schema: { $ref: \"#/components/schemas/Error\" }\n          example: { success: false, message: Unauthorized }\n    Forbidden:\n      description: Authenticated, but not an admin\n      content:\n        application/json:\n          schema: { $ref: \"#/components/schemas/Error\" }\n    NotFound:\n      description: File, key, section or item not found\n      content:\n        application/json:\n          schema: { $ref: \"#/components/schemas/Error\" }\n          example: { success: false, message: conf.yml not found }\n    ParseError:\n      description: File could not be read or parsed\n      content:\n        application/json:\n          schema: { $ref: \"#/components/schemas/Error\" }\n    TooLarge:\n      description: Body exceeds the size limit (256 KB)\n      content:\n        application/json:\n          schema: { $ref: \"#/components/schemas/Error\" }\n\n  schemas:\n    Error:\n      type: object\n      properties:\n        success: { type: boolean, enum: [false] }\n        message: { type: string }\n      required: [success, message]\n    WriteResult:\n      type: object\n      properties:\n        success: { type: boolean, enum: [true] }\n        message: { type: string }\n      required: [success, message]\n    FileList:\n      type: object\n      properties:\n        success: { type: boolean, enum: [true] }\n        files:\n          type: array\n          items: { type: string }\n      required: [success, files]\n    SectionResult:\n      allOf:\n        - $ref: \"#/components/schemas/WriteResult\"\n        - type: object\n          properties:\n            index: { type: integer, description: Array index of the section }\n            section: { $ref: \"#/components/schemas/Section\" }\n    ItemResult:\n      allOf:\n        - $ref: \"#/components/schemas/WriteResult\"\n        - type: object\n          properties:\n            index: { type: integer, description: Array index of the item }\n            item: { $ref: \"#/components/schemas/Item\" }\n\n    Config:\n      type: object\n      description: A full config file. `conf.yml` requires `sections`; sub-pages may hold any subset.\n      additionalProperties: false\n      properties:\n        pageInfo: { $ref: \"#/components/schemas/PageInfo\" }\n        appConfig: { $ref: \"#/components/schemas/AppConfig\" }\n        sections:\n          type: array\n          items: { $ref: \"#/components/schemas/Section\" }\n        pages:\n          type: array\n          items: { $ref: \"#/components/schemas/Page\" }\n    KeyValue:\n      description: The value of one top-level key.\n      oneOf:\n        - $ref: \"#/components/schemas/PageInfo\"\n        - $ref: \"#/components/schemas/AppConfig\"\n        - type: array\n          items: { $ref: \"#/components/schemas/Section\" }\n        - type: array\n          items: { $ref: \"#/components/schemas/Page\" }\n\n    PageInfo:\n      type: object\n      description: Branding shown in the header and footer.\n      additionalProperties: false\n      properties:\n        title: { type: string, description: Dashboard title }\n        description: { type: string, description: Sub-title }\n        navLinks: { type: array, description: Header navigation links, items: { type: object } }\n        footer: { type: string, description: Footer HTML or text }\n        logo: { type: string, description: Path or URL to the header logo }\n        favicon: { type: string, description: Path or URL to the favicon }\n        color: { type: string, description: Header text color }\n\n    AppConfig:\n      type: object\n      description: |\n        Global app settings (theme, layout, status checks, auth, etc.). Many fields are\n        supported. See the [configuring docs](https://github.com/Lissy93/dashy/blob/master/docs/configuring.md).\n      properties:\n        theme: { type: string, description: Default theme name }\n        layout: { type: string, enum: [horizontal, vertical, auto, masonry, sidebar], description: Section layout }\n        iconSize: { type: string, enum: [small, medium, large], description: Item icon size }\n        language: { type: string, description: UI language code }\n        startingView: { type: string, enum: [home, default, minimal, workspace], description: Initial view }\n        statusCheck: { type: boolean, description: Enable status checks globally }\n\n    SectionFields:\n      type: object\n      description: Any subset of a section's fields (used for PATCH).\n      additionalProperties: false\n      properties:\n        name:\n          type: string\n          description: Section heading (unique)\n        icon:\n          type: string\n          description: Section icon\n        displayData:\n          type: object\n          description: Per-section display options\n        items:\n          type: array\n          description: The links/apps in this section\n          items: { $ref: \"#/components/schemas/Item\" }\n        widgets:\n          type: array\n          description: Widgets shown in this section\n          items: { type: object }\n        filteredItems:\n          type: array\n          description: Items pulled from another source\n          items: { type: object }\n    Section:\n      description: A group of items, shown as a card. Requires a `name`.\n      allOf:\n        - $ref: \"#/components/schemas/SectionFields\"\n        - { type: object, required: [name] }\n\n    ItemFields:\n      type: object\n      description: Any subset of an item's fields (used for PATCH).\n      additionalProperties: false\n      properties:\n        title:\n          type: string\n          description: Display name\n        description:\n          type: string\n          description: Text shown on hover\n        icon:\n          type: string\n          description: \"Icon (URL, favicon, font-awesome, etc.)\"\n        url:\n          type: string\n          description: Target URL\n        target:\n          type: string\n          enum: [newtab, sametab, parent, top, modal, workspace, clipboard, newwindow]\n          description: How the link opens\n        provider:\n          type: string\n          description: Hosting provider name\n        id:\n          type: string\n          description: Unique identifier\n        tags:\n          type: array\n          description: Tags for filtering\n          items: { type: string }\n        hotkey:\n          type: integer\n          description: Numeric keyboard shortcut\n        rel:\n          type: string\n          description: Anchor rel attribute\n        color:\n          type: string\n          description: Text color\n        backgroundColor:\n          type: string\n          description: Background color\n        displayData:\n          type: object\n          description: Per-item display options\n        subItems:\n          type: array\n          description: Nested child items\n          items: { type: object }\n        statusCheck:\n          type: boolean\n          description: Enable status check for this item\n        statusCheckUrl:\n          type: string\n          description: Override URL to ping\n        statusCheckHeaders:\n          type: object\n          description: Headers sent with the status check\n        statusCheckAllowInsecure:\n          type: boolean\n          description: Allow invalid TLS certs\n        statusCheckAcceptCodes:\n          type: string\n          description: Extra HTTP codes treated as up\n        statusCheckMaxRedirects:\n          type: integer\n          description: Max redirects to follow\n        pingCheckEnabled:\n          type: boolean\n          description: Enable ICMP ping check\n        pingCheckHost:\n          type: string\n          description: Host to ping\n        pingCheckInterval:\n          type: integer\n          description: Ping interval (seconds)\n        pingCheckCount:\n          type: integer\n          description: Number of pings\n        pingCheckTimeout:\n          type: integer\n          description: Ping timeout (ms)\n    Item:\n      description: A single link, app or service. Requires a `title`.\n      allOf:\n        - $ref: \"#/components/schemas/ItemFields\"\n        - { type: object, required: [title] }\n\n    Page:\n      type: object\n      description: An extra config file loaded as a separate page.\n      additionalProperties: false\n      required: [name, path]\n      properties:\n        name: { type: string, description: Unique page identifier }\n        path: { type: string, description: File name or path of the page's config }\n        displayData: { type: object, description: Page visibility and display options }\n"}