102 lines
4.9 KiB
Markdown
102 lines
4.9 KiB
Markdown
# 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
|
|
|
|
```json
|
|
{
|
|
"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.
|