SparkUAPIExperimentalllms.txtРусский

Sections · 6/11

HTTP API

Transport, authorization, and complete list of /v1 routes — request, body, response, error codes.

Markdown ↗

TIP

A machine-readable description of all routes is at /openapi.json (OpenAPI 3.1). sparku openapi <address> prints the schema with one integration's fields.

Transport#

SparkU listens to HTTP/1.1 on a UNIX socket ~/.sparku/run/api.sock. The socket exists only while "Custom Integrations" is enabled in Settings and SparkU is running — if the file doesn't exist, the feature is disabled or the app is not running (see If It Doesn't Work).

Socket permissions are 0700 for the directory and 0600 for the file: access only to the same user. There is no TCP port, so requests can only be sent from the same Mac.

curl --unix-socket ~/.sparku/run/api.sock http://sparku/v1/health

The host in the URL (http://sparku/...) does not resolve — the connection still goes through the socket; the name is only needed for curl to assemble a correct request.

Request and response bodies are JSON (Content-Type: application/json; charset=utf-8). Body encoding is Content-Length; Transfer-Encoding: chunked is not supported.

Authorization#

All integration routes (/v1/integrations/{slug}/...) require a header:

Authorization: Bearer <token>

The token is issued when an integration is created (Settings → Custom Integrations → slug → Connection) or when importing .sparku (.sparku Files and Import). Without the header — 401 unauthorized; with a wrong or revoked token — 401 invalid_token.

GET /v1/health and POST /v1/imports don't require a token: the former has nothing to protect, and the latter doesn't yet know which integration the request belongs to — the integration doesn't exist yet.

Routes#

Method Path Purpose
GET /v1/health protocol and app version
GET /v1/integrations/{slug} schema of fields and current values
POST /v1/integrations/{slug}/fields partial write values (upsert)
DELETE /v1/integrations/{slug}/fields clear all values
POST /v1/integrations/{slug}/notify flash and text for a few seconds
POST /v1/integrations/{slug}/glow light spots at given places of the outline
DELETE /v1/integrations/{slug}/glow turn the spots off
POST /v1/integrations/{slug}/dialogs question to user — Questions to User
GET /v1/integrations/{slug}/events stream of taps (SSE) — Buttons and Events
POST /v1/imports import .sparku — .sparku Files and Import

A method that the route doesn't support responds 405 method_not_allowed with an Allow header. Unknown path under /v1 — 404 not_found.

GET /v1/health#

No token required. Doesn't check that the integration exists — just "SparkU is here".

curl --unix-socket ~/.sparku/run/api.sock http://sparku/v1/health
{"status": "ok", "api_version": 1, "app_version": "2.4.0"}

GET /v1/integrations/{slug}#

Schema of the integration's fields and current values (if they haven't expired by TTL).

curl --unix-socket ~/.sparku/run/api.sock \
  -H "Authorization: Bearer $SPARKU_TOKEN_DEPLOY" \
  http://sparku/v1/integrations/deploy
{
  "slug": "deploy",
  "name": "Deploy",
  "enabled": true,
  "fields": [
    {"key": "progress", "type": "progress", "label": "Progress"},
    {"key": "stage", "type": "state", "label": "Stage", "states": ["running", "done", "failed"]},
    {"key": "title", "type": "text", "label": "Service", "max_length": 60}
  ],
  "values": {"progress": 0.4, "stage": "running", "title": "api · prod"},
  "updated_at": "2026-09-28T10:15:00Z",
  "expires_at": "2026-09-28T11:15:00Z"
}

expires_at is absent from the response if values have no TTL. If values don't exist yet or have expired, values is an empty object, and updated_at/expires_at are absent.

Codes: 200, 404 unknown_integration, 401.

POST /v1/integrations/{slug}/fields#

Partial write values — upsert: listed fields change, others are left alone. null for a field clears it. Write is all or nothing: if any field fails validation, none are applied.

curl --unix-socket ~/.sparku/run/api.sock \
  -H "Authorization: Bearer $SPARKU_TOKEN_DEPLOY" \
  -H "Content-Type: application/json" \
  -d '{"progress": 0.72, "stage": "running", "title": "api · prod"}' \
  http://sparku/v1/integrations/deploy/fields
{"ok": true, "updated": ["progress", "stage", "title"], "cleared": []}

Value types in the body — see Field types. Body {"progress": null} goes into cleared, not updated.

?ttl= — how long to remember values from this write: number of seconds (?ttl=90) or relative time (?ttl=5m, ?ttl=1h30m), no more than 7 days. Without the parameter, the TTL from the integration settings applies (default — "until SparkU restart"). The countdown resets for each write. If TTL is set, the response also contains expires_at:

{"ok": true, "updated": ["progress"], "cleared": [], "expires_at": "2026-09-28T11:20:00Z"}

?bind= — bind values to a process: process (the one that sent the request), a PID (?bind=$ from a shell) or none (remove the binding). When the process exits, the values are cleared. A bound response contains bound_pid. See binding to a process.

Codes: 200, 400 empty_body, 400 invalid_json, 400 invalid_ttl, 400 invalid_bind, 409 integration_disabled, 422 invalid_fields, 401, 404.

422 invalid_fields lists each invalid field:

{"error": {"code": "invalid_fields", "message": "progress: expected 0…1, got 70",
 "fields": [{"field": "progress", "message": "progress: expected 0…1, got 70"}]}}

DELETE /v1/integrations/{slug}/fields#

Clears all values for the integration — equivalent to sparku end. Response is 204 No Content, no body.

curl --unix-socket ~/.sparku/run/api.sock -X DELETE \
  -H "Authorization: Bearer $SPARKU_TOKEN_DEPLOY" \
  http://sparku/v1/integrations/deploy/fields

Codes: 204, 401, 404. Works even for disabled integrations.

POST /v1/integrations/{slug}/notify#

Flash on the bezel and/or switch the text for a few seconds. At least one of text or state is required.

curl --unix-socket ~/.sparku/run/api.sock \
  -H "Authorization: Bearer $SPARKU_TOKEN_DEPLOY" \
  -d '{"text": "Build failed", "state": "failed", "color": "red", "duration": 4}' \
  http://sparku/v1/integrations/deploy/notify
Field Type Rule
text string truncated to 120 characters (with …)
state string value of any field with type state in the integration
color string one of semantic names (blue, red, success, …)
duration number 1…10 seconds, default 4
{"ok": true}

No more than once per 2 seconds per integration: a call sooner than that returns 429 too_many_notices with a Retry-After header (seconds until the next call is allowed).

Codes: 200, 400 invalid_json, 409 integration_disabled, 409 notify_disabled (notify is disabled in the integration settings), 422 invalid_notice, 429 too_many_notices, 401, 404.

POST /v1/integrations/{slug}/glow#

Coordinate glow: light appears at given places of the island outline and stays there until the next frame arrives. Useful for audio visualization, several parallel tasks “by section”, and meters.

curl --unix-socket ~/.sparku/run/api.sock \
  -H "Authorization: Bearer $SPARKU_TOKEN_DEPLOY" \
  -d '{"spots": [{"position": 0.2, "color": "green"},
                 {"position": 0.8, "color": "#FF3B30", "intensity": 0.6, "width": 0.15}]}' \
  "http://sparku/v1/integrations/deploy/glow?ttl=10"
Field Type Rule
spots array required, at most 64 spots; an empty array means dark
spots[].position number 0…1 left to right across the island, 0.5 is the middle under the notch
spots[].color string a semantic name (blue, red, success, …) or #RRGGBB
spots[].intensity number 0…1, default 1
spots[].width number half-width of the spot as a fraction of the island width, 0.005…1, default 0.08
intensity number outline thickness and glow strength, 0.2…3, default 1
{"ok": true}
  • Each request replaces the whole frame. Overlapping spots blend their colors.
  • No rate limit: send frames even 60 times a second, the island shows at most ~30.
  • ?ttl= — how long a frame stays lit without the next one: seconds or 5m / 1h30m, default 5 seconds. If the script dies, the glow fades by itself.
  • While a flash or status of another widget is on, it takes the shape of your spots: its colors, brightness along the outline from the spots (at least 30%). Between integrations with spots, the integration priority decides.
  • DELETE …/glow and sparku end turn the spots off immediately.

Codes: 200, 400 invalid_ttl, 409 integration_disabled, 422 invalid_glow, 401, 404.

POST /v1/integrations/{slug}/dialogs#

Question to the user on the island. Details — Questions to User.

GET /v1/integrations/{slug}/events#

Stream of taps in the card (SSE). Details — Buttons and Events.

POST /v1/imports#

Import .sparku. Details — .sparku Files and Import.

Unified error format#

Any error is JSON of the form:

{"error": {"code": "invalid_fields", "message": "human-readable text"}}

message is written for humans and assumes the client is usually a shell script: the text can be printed to the terminal as-is. Some errors add fields to the error object (for example fields for invalid_fields).

Error codes#

Code HTTP Where
not_found 404 path not under /v1 or doesn't exist
unknown_integration 404 no integration with that slug
unauthorized 401 no Authorization: Bearer … header
invalid_token 401 token doesn't belong to this integration
method_not_allowed 405 method not supported on this path
empty_body 400 request body is empty (fields)
invalid_json 400 body is not a JSON object
invalid_ttl 400 can't parse ?ttl=
invalid_bind 400 ?bind= is not process, none or the PID of a running process
invalid_fields 422 value(s) failed type validation
invalid_notice 422 notify: wrong text/state/color/duration
invalid_glow 422 glow: wrong spots, color or intensity
invalid_dialog 422 dialogs: can't parse question body
invalid_package 422 imports: can't parse .sparku file
integration_disabled 409 integration is disabled in Settings
notify_disabled 409 notify is disabled in the integration settings
dialog_pending 409 integration already has an unanswered question
import_pending 409 SparkU is already offering another import to the user
import_declined 403 user declined the offered integration
too_many_notices 429 notify more than once per 2 seconds
too_many_connections 503 more than 64 concurrent connections
unavailable 503 SparkU is not yet ready to receive requests
withdrawn 503 SparkU is closing or API was disabled while waiting
bad_request 400 / 411 / 413 / 431 / 505 HTTP violation: bad headers, no Content-Length, body over 1 MB, headers too large, unsupported version

NOTE

bad_request is a unified code for HTTP parsing errors; see the message and HTTP status for the specific reason.

Coalescing and limits#

  • fields accepts requests without rate limiting, but updates to the island appear no more than once per frame — a fast push cycle doesn't flicker intermediate values.
  • notify — no more than once per 2 seconds per integration (429 if exceeded).
  • glow — no rate limit, the island shows at most ~30 frames per second.
  • Dialog — no more than one unanswered question per integration (409 dialog_pending).
  • Import — no more than one offer per entire app at a time (409 import_pending).
  • Connections — no more than 64 concurrent per entire server (503 too_many_connections).
  • Request body — no more than 1 MB (limit accounts for the image field in base64).
  • Number history for chart and trend — no more than one point per second, 120 points per field, memory-only in the SparkU process.

Compatibility of /v1#

Inside /v1, changes are only additive: new routes and new response fields, but no changes to existing behavior. A script written today will continue to work after a SparkU update without modification.