Files
pigway-frame-flow/docs/integrations/TICKTAR.md

4.9 KiB

Optional external integration contract

Ticktar integration is optional. Frame Flow has no runtime dependency on Ticktar. No external client receives a KoKonna API key, accesses the SQLite database, or imports a vendor SDK. Web UI and external clients use the same application services.

Architecture

External client → /api/v1 → Application / DisplayService → FrameDeviceDriver → KoKonna. Configure the caller with FRAME_FLOW_URL and FRAME_FLOW_TOKEN only. No caller-specific branch exists in core. Frame Flow starts and works with no integration configuration.

Authentication and permissions

Authorization: Bearer FRAME_FLOW_TOKEN. The bootstrap token is provided through .env or a secret file; database stores SHA-256 hash only. The client model supports client_id, name, permissions, enabled, created_at, last_used_at. Permissions: devices:read, images:read, images:delete, display:write, templates:read, render:write, jobs:read, jobs:write, history:read, settings:write, admin. 0.1.0 seeds one admin token; provisioning scoped client rows is supported by middleware but multi-token management UI is not implemented. Use a trusted server-to-server connection; CORS is opt-in by exact origins, never wildcard by default.

Discovery and capabilities

GET /api/v1/devices; GET /api/v1/devices/{id}; GET /api/v1/templates. Device provides id/name/status, display geometry, power, current_content, firmware, model/model_code, timezone, last_seen_at, capabilities. Model fallback is flagged model_verified=false when the cloud omits model fields. Do not branch on driver: check capabilities upload/gallery/delete/switch/battery/color/remote_refresh. Native slideshow members/editing are unavailable in KoKonna's documented API.

Display

POST /api/v1/display, Idempotency-Key: a-unique-client-request-key

{
  "device_id": "living-room",
  "content": {
    "type": "template",
    "template": "daily-dashboard",
    "data": {"title": "Good Morning", "events": []},
    "profile": "color"
  },
  "options": {"duration": 1800, "restore_previous": true, "priority": 50},
  "source": {"client": "external-app", "request_id": "caller-trace"}
}

Content supports image (exactly one image_id/image_name/asset_id/base64), image_url (HTTPS/public DNS/exact allowlist), template. html/markdown explicitly return UNSUPPORTED_CAPABILITY. For upload-only use device /upload with display_now=false; this is safe local staging because KoKonna cloud upload always activates an image.

Returns HTTP 202: {operation_id, job_id, status:"accepted"}. job_id is a legacy acceptance alias for operation_id, not a scheduler job ID.

Render and operations

POST /api/v1/render with template content returns 202 and a local rendered asset when completed; no device write. GET /api/v1/operations/{operation_id}: QUEUED → RENDERING → UPLOADING → SYNCING → COMPLETED / FAILED / UNKNOWN. Result includes image_id or asset_id/url. SYNCING confirms imageId plus synced through read-only device status; there is no documented physical-screen telemetry. Token must own the operation or be admin. Never interpret HTTP 202 as refreshed.

Idempotency

Key is scoped to authenticated client, retained seven days. Same key and same normalized action/body returns the same operation, even across restart. A different body with the same key returns CONFLICT (409). Do not retry UNKNOWN with a new key. Cloud writes have no automatic retries. UNKNOWN blocks further device writes; admin /operations/{id}/reconcile reads device/gallery and may acknowledge a manually verified outcome without reissuing a write.

Errors and tracing

All API errors: {error:{code,message,request_id,details:{}}}. Stable codes include INVALID_REQUEST, UNAUTHORIZED, FORBIDDEN, DEVICE_NOT_FOUND, DEVICE_OFFLINE, UNSUPPORTED_CAPABILITY, RENDER_FAILED, UPLOAD_FAILED, WRITE_RESULT_UNKNOWN, RATE_LIMITED, INTERNAL_ERROR, CONFIG_REQUIRED, WRITE_DISABLED, CONFLICT, NOT_FOUND, CLOUD_UNAVAILABLE. Use code, not message text. Validation does not echo inputs. X-Request-ID accepts a bounded safe identifier; it is returned on the response. The authenticated client is authoritative; source.client is optional metadata. History stores client_id/request_id/operation_id/device_id/action/result/timestamp. No internal endpoints, headers, stacks, paths or secrets are returned.

Future webhook

SQLite events and webhook_subscriptions reserve operation.completed/failed, display.changed, device.online/offline/low_battery events. 0.1.0 emits operation/display events only; no callback delivery, retry worker or registration API is implemented. Future deliveries should use signed payloads and bounded retries. OAuth, tenancy, RBAC management and generated SDKs are future work.

Compatibility

/openapi.json and /docs are the contract. Snapshot tests cover paths, response codes, standard device fields, security and operation enums. Static snapshot is also supplied in docs/openapi.json. Breaking changes require /api/v2.