{
  "components" : {
    "parameters" : {
      "bind" : {
        "description" : "Clear the values when a process exits: `process` (the sender), a PID, or `none` to remove the binding.",
        "in" : "query",
        "name" : "bind",
        "required" : false,
        "schema" : {
          "examples" : [
            "process",
            "4211",
            "none"
          ],
          "type" : "string"
        }
      },
      "glowTtl" : {
        "description" : "How long the glow frame stays lit without a new one: seconds or 5m / 1h30m. Default: 5 seconds.",
        "in" : "query",
        "name" : "ttl",
        "required" : false,
        "schema" : {
          "examples" : [
            "2",
            "30",
            "1h"
          ],
          "type" : "string"
        }
      },
      "slug" : {
        "description" : "Integration address from SparkU Settings → Custom Integrations.",
        "in" : "path",
        "name" : "slug",
        "required" : true,
        "schema" : {
          "pattern" : "^[a-z0-9][a-z0-9-]{0,31}$",
          "type" : "string"
        }
      },
      "ttl" : {
        "description" : "How long to keep the values of this write: seconds or 5m / 1h30m, up to 7 days. Default: the integration's setting.",
        "in" : "query",
        "name" : "ttl",
        "required" : false,
        "schema" : {
          "examples" : [
            "90",
            "5m",
            "1h30m"
          ],
          "type" : "string"
        }
      }
    },
    "responses" : {
      "Error" : {
        "content" : {
          "application/json" : {
            "schema" : {
              "$ref" : "#/components/schemas/Error"
            }
          }
        },
        "description" : "Error with a machine code and a human-readable message"
      }
    },
    "schemas" : {
      "Answer" : {
        "additionalProperties" : false,
        "properties" : {
          "answer" : {
            "enum" : [
              "chose",
              "custom",
              "accepted",
              "declined",
              "expired",
              "withdrawn"
            ],
            "type" : "string"
          },
          "comment" : {
            "description" : "Comment on a decline",
            "type" : "string"
          },
          "options" : {
            "description" : "Chosen option ids (chose)",
            "items" : {
              "type" : "string"
            },
            "type" : "array"
          },
          "text" : {
            "description" : "Free-text answer (custom)",
            "type" : "string"
          }
        },
        "required" : [
          "answer"
        ],
        "type" : "object"
      },
      "ControlEvent" : {
        "additionalProperties" : false,
        "properties" : {
          "at" : {
            "format" : "date-time",
            "type" : "string"
          },
          "control" : {
            "type" : "string"
          },
          "integration" : {
            "type" : "string"
          },
          "value" : {

          }
        },
        "required" : [
          "integration",
          "control",
          "at"
        ],
        "type" : "object"
      },
      "Dialog" : {
        "additionalProperties" : false,
        "properties" : {
          "accept" : {
            "description" : "Confirm button (no options)",
            "type" : "string"
          },
          "attachment" : {
            "description" : "Absolute path to a file shown with the question",
            "type" : "string"
          },
          "comment" : {
            "description" : "Let the user add a comment when declining",
            "type" : "boolean"
          },
          "custom" : {
            "default" : false,
            "description" : "Allow a free-text answer",
            "type" : "boolean"
          },
          "decline" : {
            "description" : "Decline button (no options)",
            "type" : "string"
          },
          "message" : {
            "maxLength" : 600,
            "type" : "string"
          },
          "multiple" : {
            "default" : false,
            "type" : "boolean"
          },
          "options" : {
            "items" : {
              "oneOf" : [
                {
                  "type" : "string"
                },
                {
                  "additionalProperties" : false,
                  "properties" : {
                    "detail" : {
                      "type" : "string"
                    },
                    "id" : {
                      "type" : "string"
                    },
                    "label" : {
                      "type" : "string"
                    }
                  },
                  "required" : [
                    "id",
                    "label"
                  ],
                  "type" : "object"
                }
              ]
            },
            "maxItems" : 9,
            "type" : "array"
          },
          "timeout" : {
            "default" : 600,
            "oneOf" : [
              {
                "maximum" : 86400,
                "minimum" : 5,
                "type" : "number"
              },
              {
                "examples" : [
                  "90s",
                  "10m",
                  "2h"
                ],
                "type" : "string"
              }
            ]
          },
          "title" : {
            "type" : "string"
          }
        },
        "required" : [
          "message"
        ],
        "type" : "object"
      },
      "Error" : {
        "additionalProperties" : false,
        "properties" : {
          "error" : {
            "additionalProperties" : false,
            "properties" : {
              "code" : {
                "enum" : [
                  "bad_request",
                  "dialog_pending",
                  "empty_body",
                  "import_declined",
                  "import_pending",
                  "integration_disabled",
                  "invalid_bind",
                  "invalid_dialog",
                  "invalid_fields",
                  "invalid_glow",
                  "invalid_json",
                  "invalid_notice",
                  "invalid_package",
                  "invalid_token",
                  "invalid_ttl",
                  "method_not_allowed",
                  "not_found",
                  "notify_disabled",
                  "too_many_connections",
                  "too_many_notices",
                  "unauthorized",
                  "unavailable",
                  "unknown_integration",
                  "withdrawn"
                ],
                "type" : "string"
              },
              "fields" : {
                "items" : {
                  "additionalProperties" : false,
                  "properties" : {
                    "field" : {
                      "type" : "string"
                    },
                    "message" : {
                      "type" : "string"
                    }
                  },
                  "type" : "object"
                },
                "type" : "array"
              },
              "message" : {
                "type" : "string"
              }
            },
            "required" : [
              "code",
              "message"
            ],
            "type" : "object"
          }
        },
        "required" : [
          "error"
        ],
        "type" : "object"
      },
      "Fields" : {
        "additionalProperties" : true,
        "description" : "Field key → value. The accepted value depends on the field type; null clears the field. Run `sparku openapi <slug>` for the exact schema of one integration.",
        "examples" : [
          {
            "eta" : "+5m",
            "progress" : 0.40000000000000002,
            "stage" : "running",
            "title" : "prod"
          }
        ],
        "type" : "object"
      },
      "Glow" : {
        "additionalProperties" : false,
        "properties" : {
          "intensity" : {
            "default" : 1,
            "description" : "Outline thickness and glow strength multiplier",
            "maximum" : 3,
            "minimum" : 0.2,
            "type" : "number"
          },
          "spots" : {
            "items" : {
              "additionalProperties" : false,
              "properties" : {
                "color" : {
                  "anyOf" : [
                    {
                      "enum" : [
                        "blue",
                        "green",
                        "orange",
                        "red",
                        "purple",
                        "pink",
                        "yellow",
                        "teal",
                        "indigo",
                        "gray",
                        "success",
                        "warning",
                        "error"
                      ]
                    },
                    {
                      "pattern" : "^#[0-9A-Fa-f]{6}$"
                    }
                  ],
                  "description" : "A palette name or #RRGGBB",
                  "type" : "string"
                },
                "intensity" : {
                  "default" : 1,
                  "maximum" : 1,
                  "minimum" : 0,
                  "type" : "number"
                },
                "position" : {
                  "description" : "Horizontal position along the island: 0 — left edge, 1 — right edge",
                  "maximum" : 1,
                  "minimum" : 0,
                  "type" : "number"
                },
                "width" : {
                  "default" : 0.08,
                  "description" : "Half-width of the spot as a fraction of the island width; overlapping spots blend",
                  "maximum" : 1,
                  "minimum" : 0.005,
                  "type" : "number"
                }
              },
              "required" : [
                "position",
                "color"
              ],
              "type" : "object"
            },
            "maxItems" : 64,
            "type" : "array"
          }
        },
        "required" : [
          "spots"
        ],
        "type" : "object"
      },
      "Health" : {
        "additionalProperties" : false,
        "properties" : {
          "api_version" : {
            "type" : "integer"
          },
          "app_version" : {
            "type" : "string"
          },
          "status" : {
            "const" : "ok"
          }
        },
        "required" : [
          "status",
          "api_version",
          "app_version"
        ],
        "type" : "object"
      },
      "Imported" : {
        "additionalProperties" : false,
        "properties" : {
          "name" : {
            "type" : "string"
          },
          "slug" : {
            "type" : "string"
          },
          "token" : {
            "type" : "string"
          }
        },
        "required" : [
          "slug",
          "name",
          "token"
        ],
        "type" : "object"
      },
      "Integration" : {
        "additionalProperties" : false,
        "properties" : {
          "enabled" : {
            "type" : "boolean"
          },
          "expires_at" : {
            "format" : "date-time",
            "type" : "string"
          },
          "fields" : {
            "items" : {
              "additionalProperties" : false,
              "properties" : {
                "key" : {
                  "type" : "string"
                },
                "label" : {
                  "type" : "string"
                },
                "max" : {
                  "type" : "number"
                },
                "max_length" : {
                  "type" : "integer"
                },
                "min" : {
                  "type" : "number"
                },
                "states" : {
                  "items" : {
                    "type" : "string"
                  },
                  "type" : "array"
                },
                "type" : {
                  "enum" : [
                    "text",
                    "number",
                    "progress",
                    "bool",
                    "state",
                    "time",
                    "image",
                    "url",
                    "list"
                  ],
                  "type" : "string"
                },
                "unit" : {
                  "type" : "string"
                }
              },
              "required" : [
                "key",
                "type",
                "label"
              ],
              "type" : "object"
            },
            "type" : "array"
          },
          "name" : {
            "type" : "string"
          },
          "slug" : {
            "type" : "string"
          },
          "updated_at" : {
            "format" : "date-time",
            "type" : "string"
          },
          "values" : {
            "type" : "object"
          }
        },
        "required" : [
          "slug",
          "name",
          "enabled",
          "fields",
          "values"
        ],
        "type" : "object"
      },
      "Notice" : {
        "additionalProperties" : false,
        "properties" : {
          "color" : {
            "enum" : [
              "blue",
              "green",
              "orange",
              "red",
              "purple",
              "pink",
              "yellow",
              "teal",
              "indigo",
              "gray",
              "success",
              "warning",
              "error"
            ],
            "type" : "string"
          },
          "duration" : {
            "default" : 4,
            "maximum" : 10,
            "minimum" : 1,
            "type" : "number"
          },
          "state" : {
            "description" : "A value of one of the integration's state fields",
            "type" : "string"
          },
          "text" : {
            "maxLength" : 120,
            "type" : "string"
          }
        },
        "type" : "object"
      },
      "Ok" : {
        "additionalProperties" : false,
        "properties" : {
          "ok" : {
            "const" : true
          }
        },
        "required" : [
          "ok"
        ],
        "type" : "object"
      },
      "Package" : {
        "additionalProperties" : false,
        "properties" : {
          "format" : {
            "const" : "sparku.integration"
          },
          "integration" : {
            "description" : "Integration schema without id, token and state",
            "type" : "object"
          },
          "version" : {
            "const" : 1
          }
        },
        "required" : [
          "format",
          "version",
          "integration"
        ],
        "type" : "object"
      },
      "Pushed" : {
        "additionalProperties" : false,
        "properties" : {
          "bound_pid" : {
            "type" : "integer"
          },
          "cleared" : {
            "items" : {
              "type" : "string"
            },
            "type" : "array"
          },
          "expires_at" : {
            "format" : "date-time",
            "type" : "string"
          },
          "ok" : {
            "const" : true
          },
          "updated" : {
            "items" : {
              "type" : "string"
            },
            "type" : "array"
          }
        },
        "required" : [
          "ok",
          "updated",
          "cleared"
        ],
        "type" : "object"
      }
    },
    "securitySchemes" : {
      "bearer" : {
        "description" : "Integration token (spk_…), shown in the integration's Connection section.",
        "scheme" : "bearer",
        "type" : "http"
      }
    }
  },
  "info" : {
    "description" : "Local API of SparkU (macOS notch app): scripts push values to a custom integration, listen to button presses and ask the user questions. Transport: HTTP/1.1 over the UNIX socket ~/.sparku/run/api.sock (curl --unix-socket ~/.sparku/run/api.sock http://sparku/v1/health). The socket exists only while Custom Integrations are turned on. Docs: /docs/api/, /llms.txt.",
    "title" : "SparkU API",
    "version" : "1",
    "x-sparku-cli-version" : "dev"
  },
  "openapi" : "3.1.0",
  "paths" : {
    "/v1/health" : {
      "get" : {
        "operationId" : "health",
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Health"
                }
              }
            },
            "description" : "SparkU is running"
          }
        },
        "security" : [

        ],
        "summary" : "Protocol and app version"
      }
    },
    "/v1/imports" : {
      "post" : {
        "description" : "No token: the integration doesn't exist yet. The connection stays open until the user answers in SparkU Settings.",
        "operationId" : "importIntegration",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/Package"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "201" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Imported"
                }
              }
            },
            "description" : "Added; the token is returned once"
          },
          "403" : {
            "$ref" : "#/components/responses/Error"
          },
          "409" : {
            "$ref" : "#/components/responses/Error"
          },
          "422" : {
            "$ref" : "#/components/responses/Error"
          }
        },
        "security" : [

        ],
        "summary" : "Offer a .sparku integration; waits for the user's consent"
      }
    },
    "/v1/integrations/{slug}" : {
      "get" : {
        "operationId" : "describeIntegration",
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Integration"
                }
              }
            },
            "description" : "Schema and values"
          },
          "401" : {
            "$ref" : "#/components/responses/Error"
          },
          "404" : {
            "$ref" : "#/components/responses/Error"
          }
        },
        "summary" : "Fields and current values"
      },
      "parameters" : [
        {
          "$ref" : "#/components/parameters/slug"
        }
      ]
    },
    "/v1/integrations/{slug}/dialogs" : {
      "parameters" : [
        {
          "$ref" : "#/components/parameters/slug"
        }
      ],
      "post" : {
        "description" : "One pending question per integration. The connection stays open up to `timeout`; if the client disconnects, the question is withdrawn.",
        "operationId" : "ask",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/Dialog"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Answer"
                }
              }
            },
            "description" : "The user's answer, or expired / withdrawn"
          },
          "401" : {
            "$ref" : "#/components/responses/Error"
          },
          "404" : {
            "$ref" : "#/components/responses/Error"
          },
          "409" : {
            "$ref" : "#/components/responses/Error"
          },
          "422" : {
            "$ref" : "#/components/responses/Error"
          }
        },
        "summary" : "Ask the user; the response arrives when they answer"
      }
    },
    "/v1/integrations/{slug}/events" : {
      "get" : {
        "description" : "`event: hello` with the controls, then `event: control` with a ControlEvent per press; `: ping` every 15 s. At-most-once, no queue.",
        "operationId" : "events",
        "responses" : {
          "200" : {
            "content" : {
              "text/event-stream" : {
                "schema" : {
                  "type" : "string"
                },
                "x-event-data" : {
                  "$ref" : "#/components/schemas/ControlEvent"
                }
              }
            },
            "description" : "SSE stream"
          },
          "401" : {
            "$ref" : "#/components/responses/Error"
          },
          "404" : {
            "$ref" : "#/components/responses/Error"
          }
        },
        "summary" : "Server-sent events: button presses in the card"
      },
      "parameters" : [
        {
          "$ref" : "#/components/parameters/slug"
        }
      ]
    },
    "/v1/integrations/{slug}/fields" : {
      "delete" : {
        "operationId" : "clearFields",
        "responses" : {
          "204" : {
            "description" : "Cleared"
          },
          "401" : {
            "$ref" : "#/components/responses/Error"
          },
          "404" : {
            "$ref" : "#/components/responses/Error"
          }
        },
        "summary" : "Clear all values (sparku end)"
      },
      "parameters" : [
        {
          "$ref" : "#/components/parameters/slug"
        }
      ],
      "post" : {
        "description" : "All or nothing: one invalid field rejects the whole write with 422.",
        "operationId" : "pushFields",
        "parameters" : [
          {
            "$ref" : "#/components/parameters/ttl"
          },
          {
            "$ref" : "#/components/parameters/bind"
          }
        ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/Fields"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Pushed"
                }
              }
            },
            "description" : "Written"
          },
          "400" : {
            "$ref" : "#/components/responses/Error"
          },
          "401" : {
            "$ref" : "#/components/responses/Error"
          },
          "404" : {
            "$ref" : "#/components/responses/Error"
          },
          "409" : {
            "$ref" : "#/components/responses/Error"
          },
          "422" : {
            "$ref" : "#/components/responses/Error"
          }
        },
        "summary" : "Write field values (partial upsert; null clears a field)"
      }
    },
    "/v1/integrations/{slug}/glow" : {
      "delete" : {
        "operationId" : "clearGlow",
        "responses" : {
          "204" : {
            "description" : "Turned off"
          },
          "401" : {
            "$ref" : "#/components/responses/Error"
          },
          "404" : {
            "$ref" : "#/components/responses/Error"
          }
        },
        "summary" : "Turn the spots off"
      },
      "parameters" : [
        {
          "$ref" : "#/components/parameters/slug"
        }
      ],
      "post" : {
        "description" : "Each call replaces the whole frame. No rate limit: send frames as often as needed (e.g. an audio visualizer); SparkU draws at most ~30 per second. While a regular flash or status glow is on, it takes the shape of the spots: its own colors and motion, brightness along the outline from the spots. The frame fades after `ttl` without a new one (default 5 s).",
        "operationId" : "glow",
        "parameters" : [
          {
            "$ref" : "#/components/parameters/glowTtl"
          }
        ],
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/Glow"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Ok"
                }
              }
            },
            "description" : "Shown"
          },
          "400" : {
            "$ref" : "#/components/responses/Error"
          },
          "401" : {
            "$ref" : "#/components/responses/Error"
          },
          "404" : {
            "$ref" : "#/components/responses/Error"
          },
          "409" : {
            "$ref" : "#/components/responses/Error"
          },
          "422" : {
            "$ref" : "#/components/responses/Error"
          }
        },
        "summary" : "Light spots at given positions along the notch outline"
      }
    },
    "/v1/integrations/{slug}/notify" : {
      "parameters" : [
        {
          "$ref" : "#/components/parameters/slug"
        }
      ],
      "post" : {
        "description" : "At most once per 2 seconds per integration; otherwise 429 with Retry-After.",
        "operationId" : "notify",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/Notice"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Ok"
                }
              }
            },
            "description" : "Shown"
          },
          "400" : {
            "$ref" : "#/components/responses/Error"
          },
          "401" : {
            "$ref" : "#/components/responses/Error"
          },
          "404" : {
            "$ref" : "#/components/responses/Error"
          },
          "409" : {
            "$ref" : "#/components/responses/Error"
          },
          "422" : {
            "$ref" : "#/components/responses/Error"
          },
          "429" : {
            "$ref" : "#/components/responses/Error"
          }
        },
        "summary" : "Flash the notch and show a line for a few seconds"
      }
    }
  },
  "security" : [
    {
      "bearer" : [

      ]
    }
  ],
  "servers" : [
    {
      "description" : "UNIX socket ~/.sparku/run/api.sock — the host name is ignored",
      "url" : "http://sparku"
    }
  ],
  "x-unix-socket" : "~/.sparku/run/api.sock"
}
