Zum Hauptinhalt springen

API-Beispiele

Nutzen Sie diese Seite, um die richtige Anfrageform für jeden Erweiterungsablauf zu kopieren.

  • Beispiele für Iframe-Bridges verwenden postMessage-Aktionen wie session.get, state.get und files.get.
  • Privilegierte Beispiele verwenden Ihr eigenes Erweiterungs-Backend mit Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY und x-chastify-main-token.
  • Senden Sie niemals einen Entwickler-API-Schlüssel an iFrame- oder Browsercode.
info

Die iFrame-Brücke dient dem UI-Bootstrap und risikoarmen Sitzungsoperationen. Sie wird verwendet, um Kontext zu lesen, den Status von Erweiterungen zu speichern und Datei-URLs aufzulösen.

vorsicht

Der Code von Browser-iFrames wird vom Benutzer gesteuert. Verwenden Sie keine iFrame-Bridge-Anfragen, um die Zeit zu ändern, Aufgaben abzuschließen, Entsperrsperren zu entfernen, Laufzeitdateien hochzuladen oder zu löschen, Benachrichtigungen zu senden, Protokolle zu schreiben oder Geräte zu steuern.

Anfrage- und Antwortformat

Sichere iFrame-Bridge-Aktionen werden innerhalb dieses Bridge-Anforderungs-Envelopes gesendet:

{
"type": "chastify:ext:req", // required
"v": 1, // protocol version
"id": "req-123", // your unique request id
"nonce": "from-iframe-hash",
"action": "session.get",
"payload": {}
}

Und Sie erhalten:

{
"type": "chastify:ext:resp",
"v": 1,
"id": "req-123", // same id you sent
"ok": true,
"data": {}
}

Wenn ok gleich false ist, prüfe error.code und error.message.

Die reinen Backend-Beispiele verwenden normale HTTPS-Anfragen von Ihrem Server. Sie werden nicht über die iFrame-Brücke gesendet.

Sitzung und Kontext

session.get

Verwenden Sie dies als Erstes in nahezu jedem Erweiterungsablauf.

Was es bewirkt:

  • Überprüft, ob Ihre Brückenverbindung funktioniert.
  • Gibt den Sitzungskontext zurück (Sperrstatus, Rolle, Erweiterungskonfiguration, Fähigkeiten).
  • Gibt Informationen zur Gerätefunktionalität zurück, die Ihre Benutzeroberfläche verwenden kann, bevor Sie Ihr Backend auffordern, device.command aufzurufen.

Wofür es verwendet wird:

  • Bootstrapping Ihrer Benutzeroberfläche.
  • Funktionen basierend auf Rolle und Berechtigungen aktivieren/deaktivieren.
  • Vor dem Rendern der Gerätesteuerung werden die unterstützten Gerätebefehle geprüft.

Beispiel einer Aktionsnutzlast:

{
"action": "session.get",
"payload": {}
}

Beispielhafter Auszug aus der Antwort von session.get (Laufzeit-Sperrdaten):

{
"ok": true,
"data": {
"lockData": {
"frozen": false,
"unlockable": false,
"trusted": true,
"taskAssigned": true,
"timeLockedSeconds": 1420,
"timeRemainingSeconds": 27800,
"maxTimeRemainingSeconds": 86400,
"taskPoints": 12,
"taskPointsRequired": 20,
"lockTitle": "Weekend Challenge",
"wearerUsername": "alice",
"keyholderUsername": "kh_bob",
"wearerLastSeenTimestamp": 1739640505123,
"keyholderLastSeenTimestamp": null
}
}
}

Datenschutzhinweise:

  • wearerLastSeenTimestamp / keyholderLastSeenTimestamp werden nur zurückgegeben, wenn der Benutzer showOnlineStatus !== false besitzt.
  • Wenn die Online-Statusanzeige deaktiviert ist, lauten diese Felder null.

Backend-Erweiterungsabläufe

Diese Beispiele veranschaulichen das sichere Produktionsmuster:

  1. Der iFrame liest mainToken und sessionId aus der Hash-Nutzlast.
  2. Der iFrame sendet sie an Ihr Backend.
  3. Ihr Backend validiert Ihren eigenen Spiel-/Aufgaben-/Geschäftsstatus.
  4. Ihr Backend ruft Chastify mit einem anwendungsbezogenen Entwickler-API-Schlüssel plus x-chastify-main-token auf.

Verwenden Sie sessionId nicht allein zur Authentifizierung. Behandeln Sie mainToken als für den Browser sichtbaren Startkontext und Ihren Entwickler-API-Schlüssel als ein nur im Backend gespeichertes Geheimnis.

warnung

Ihr Backend muss seinen eigenen Spiel-/Aufgabenstatus validieren, bevor Chastify aufgerufen wird. Die Übergabe von mainToken und sessionId aus dem iFrame identifiziert lediglich die geöffnete Erweiterungssitzung; sie beweist nicht, dass der Benutzer eine Herausforderung abgeschlossen hat.

Sitzungskontext sicher abrufen

Ihr iFrame kann die sichere Bridge session.get für den UI-Bootstrap verwenden. Falls Ihr Backend denselben Kontext benötigt, bevor es eine privilegierte Aktion ausführt, rufen Sie ihn mit beiden Anmeldeinformationen von Ihrem Backend ab.

Iframe:

const hash = JSON.parse(decodeURIComponent(window.location.hash.slice(1)));

await fetch("/api/my-extension/session-context", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
mainToken: hash.mainToken,
sessionId: hash.sessionId
})
});

Ihr Backend:

app.post("/api/my-extension/session-context", async (req, res) => {
const { mainToken, sessionId } = req.body;

const response = await fetch(
`https://chastify.net/api/extensions/sessions/${encodeURIComponent(sessionId)}`,
{
headers: {
Authorization: `Bearer ${process.env.CHASTIFY_APP_DEVELOPER_KEY}`,
"x-chastify-main-token": mainToken
}
}
);

if (!response.ok) {
return res.status(response.status).json(await response.json());
}

const context = await response.json();

res.json({
role: context.role,
config: context.extensionConfig,
lockData: context.lockData
});
});

Zeit über ein Erweiterungs-Backend anwenden

Der Browser kann Ihr Backend auffordern, eine Belohnung oder Bestrafung anzuwenden, darf aber nicht entscheiden, ob die Aktion gültig ist. Überprüfen Sie die Aktion daher zuerst serverseitig.

app.post("/api/my-extension/apply-reward", async (req, res) => {
const { mainToken, sessionId, runId } = req.body;

const run = await db.gameRuns.findUnique({ where: { id: runId } });
if (!run || run.sessionId !== sessionId || !run.serverVerifiedWin) {
return res.status(403).json({ error: "game_not_verified" });
}

const response = await fetch(
`https://chastify.net/api/extensions/sessions/${encodeURIComponent(sessionId)}/action`,
{
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `Bearer ${process.env.CHASTIFY_APP_DEVELOPER_KEY}`,
"x-chastify-main-token": mainToken
},
body: JSON.stringify({
name: "remove_time",
params: 300
})
}
);

if (!response.ok) {
return res.status(response.status).json(await response.json());
}

res.json(await response.json());
});

Verwenden Sie add_time für Bestrafungen und remove_time für Belohnungen.

Serverbestätigter Spielabschluss

Bei Spielen wie Simon Says sollte man einem vom Client gemeldeten Sieg nicht vertrauen. Erstellen Sie den Spielablauf serverseitig, speichern Sie die erwartete Sequenz oder deren Hash und überprüfen Sie die übermittelte Eingabe, bevor Sie Chastify aufrufen.

Ein im Browser sichtbares Memory-Spiel kann nicht beweisen, dass ein Mensch ehrlich gespielt hat, da der Browser die Sequenz empfangen muss, um das Spiel darzustellen. Die Serververifizierung verhindert weiterhin gefälschte Chastify-Mutationen und ermöglicht es Ihrem Backend, Lauf-IDs, Ablaufzeit, Schwierigkeitsgrad, Spielrhythmus, Punktevergabe und Wiederholungsschutz durchzusetzen, bevor Belohnungen oder Strafen angewendet werden.

app.post("/api/simon/runs", async (req, res) => {
const { mainToken, sessionId } = req.body;
await verifySessionLaunch({ mainToken, sessionId });

const sequence = createSimonSequence();

const run = await db.simonRuns.create({
data: {
sessionId,
sequenceHash: hashSequence(sequence),
expiresAt: new Date(Date.now() + 5 * 60_000)
}
});

res.json({
runId: run.id,
sequence
});
});

app.post("/api/simon/runs/:runId/complete", async (req, res) => {
const { mainToken, sessionId, input } = req.body;
await verifySessionLaunch({ mainToken, sessionId });

const run = await db.simonRuns.findUnique({ where: { id: req.params.runId } });

if (!run || run.sessionId !== sessionId || run.expiresAt < new Date()) {
return res.status(403).json({ error: "run_invalid" });
}

const won = hashSequence(input) === run.sequenceHash;
await db.simonRuns.update({
where: { id: run.id },
data: { completedAt: new Date(), serverVerifiedWin: won }
});

if (won) {
await fetch(`https://chastify.net/api/extensions/sessions/${encodeURIComponent(sessionId)}/requirements/progress`, {
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `Bearer ${process.env.CHASTIFY_APP_DEVELOPER_KEY}`,
"x-chastify-main-token": mainToken
},
body: JSON.stringify({
key: "simon_says_wins",
amount: 1
})
});
}

res.json({ won });
});

Das genaue Speicherschema für die Ausführung liegt in Ihrer Verantwortung. verifySessionLaunch sollte Chastify mit Ihrem anwendungsbezogenen Entwickler-API-Schlüssel und x-chastify-main-token aufrufen, bevor sessionId vertraut wird. Wichtig ist, dass Änderungen an Chastify erst erfolgen, nachdem Ihr Backend den Start und das Ergebnis verifiziert hat.

Geplante Anforderungen

Ihr Backend verwaltet Zeitpläne, Taktprüfungen und Nachweisvalidierungen. Verwenden Sie Chastify nur, um vertrauenswürdige Fortschritte zu protokollieren oder Entsperrsperren zu aktualisieren, nachdem Ihr Backend die Erfüllung der Anforderung bestätigt hat.

async function recordDailyRequirementProgress({ sessionId, mainToken, userId }) {
const completed = await db.dailyCheckins.exists({
where: {
userId,
day: new Date().toISOString().slice(0, 10),
verified: true
}
});

if (!completed) return;

await fetch(`https://chastify.net/api/extensions/sessions/${encodeURIComponent(sessionId)}/requirements/progress`, {
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `Bearer ${process.env.CHASTIFY_APP_DEVELOPER_KEY}`,
"x-chastify-main-token": mainToken
},
body: JSON.stringify({
key: "daily_checkin",
amount: 1
})
});
}

Die aktuellen APIs für installierte Erweiterungssitzungen benötigen ein gültiges iFrame-Starttoken im Format x-chastify-main-token. Starttoken verfallen derzeit nach 10 Stunden. Für geplante Aufgaben, die nach Ablauf des Tokens ausgeführt werden, speichern Sie einen eigenen Nachweis über den ausstehenden Status und übermitteln Sie den Fortschritt beim nächsten gültigen Erweiterungsstart oder verwenden Sie einen Server-Workflow, der für unbeaufsichtigte Hintergrundprozesse ausgelegt ist.

vorsicht

Behandeln Sie geplante Jobs nicht automatisch als vertrauenswürdig, nur weil sie auf Ihrem Server ausgeführt werden. Der Job benötigt weiterhin serverseitigen Nachweis, Überprüfung der Ausführungsfrequenz, Schutz vor Replay-Angriffen und einen gültigen Autorisierungspfad (Chastify), bevor der Fortschritt der Anforderungen protokolliert werden kann.

Benachrichtigungen

notifications.custom

Verwenden Sie dies von Ihrem Erweiterungs-Backend aus, um eine benutzerdefinierte Erweiterungsbenachrichtigung an den Träger, den Schlüsselhalter oder beide zu senden.

Endpunkt:

POST /api/extensions/sessions/:sessionId/notifications/custom
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH

Beispieltext:

{
"title": "Extension Reminder",
"message": "Your next challenge is ready.",
"showPageOverlay": false,
"target": "both"
}

Anmerkungen:

  • showPageOverlay ist standardmäßig false.
  • target ist standardmäßig wearer.
  • Die API erstellt eine Benachrichtigung vom Typ extension_app_message.

Erweiterungsstatus

Der Erweiterungsstatus sind Ihre erweiterungseigenen JSON-Daten für die aktuelle Sperrsitzung. Statusänderungen sind ausschließlich serverseitig möglich und erfordern einen anwendungsbezogenen Entwickler-API-Schlüssel sowie die Sitzungskennung mainToken. Iframes können den Status mit state.get lesen, aber nicht direkt schreiben.

state.put

Was es bewirkt:

  • Ersetzt das gesamte Statusobjekt durch das neue Objekt data.
  • Erfordert Backend-Zugangsdaten.

Wann verwenden?

  • Erster Speicherstand.
  • Vollständiges Überschreiben, wenn der neue Zustand bereits vollständig vorhanden ist.

Beispiel:

curl -X PUT "https://chastify.net/api/extensions/sessions/$SESSION_ID/state" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"counter": 1,
"notes": "first test"
}
}'

Feldnotizen:

  • payload.data: ein beliebiger gültiger JSON-Wert/ein beliebiges gültiges JSON-Objekt, das Ihre Erweiterung benötigt.
  • Avoid Mongo-unsafe key names ($ prefix or keys containing .).
  • Der Status ist sitzungsbezogen und durch den stateMaxBytes der Erweiterungs-App größenbegrenzt; der Standardwert beträgt 64 KiB.
  • Speichern Sie Datei-IDs im Status, nicht Binärdateien oder signierte URLs. Verwenden Sie files.get, um signierte URLs vor dem Rendern von Medien zu aktualisieren.

state.patch

Was es bewirkt:

  • Wendet einen JSON-Merge-Patch auf den bestehenden Zustand an.
  • Es müssen nur die geänderten Schlüssel gesendet werden.
  • Erfordert Backend-Zugangsdaten.

Wann verwenden?

  • Schrittweise Aktualisierungen basierend auf Benutzerinteraktionen.
  • Ein Feld aktualisieren, ohne alles erneut zu senden.

Beispiel:

curl -X PATCH "https://chastify.net/api/extensions/sessions/$SESSION_ID/state" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"counter": 2
}
}'

state.get

Was es bewirkt:

  • Liest den aktuellen Erweiterungsstatus.
  • Verfügbar über die iFrame-Bridge.

Wann verwenden?

  • Beim Laden des iFrames.
  • Nach dem Schreiben, falls Sie die lokale Benutzeroberfläche erneut synchronisieren möchten.

Beispiel:

{
"action": "state.get",
"payload": {}
}

Dateispeicherung

Verwenden Sie files.* für Binärmedien wie Puzzlebilder, generierte Vorschauen oder Herausforderungsfotos. Speichern Sie den zurückgegebenen Wert file.id im Backend oder in Ihrer eigenen Datenbank. Rendern Sie mit file.signedUrl und aktualisieren Sie die signierte URL mit files.get, sobald der iFrame später geladen wird.

Einrichtungsbildschirme, die vor dem Beenden einer Sperre/Sitzung ausgeführt werden, verwenden temporäre Uploads anstelle von files.upload. Die temporären Dateien sind so lange temporär, bis die Erweiterungskonfiguration mit einer provider: "chastify_storage"- und einer fileId-Referenz gespeichert wird. Chastify beansprucht diese Dateien automatisch beim Speichern der Sperre/Vorlage; es erfolgt kein separater Aufruf im Browser.

Beispiel für die Aufteilung von Status/Datei aus Ihrem Backend:

curl -X PATCH "https://chastify.net/api/extensions/sessions/$SESSION_ID/state" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"puzzleImageFileId": "file_record_id"
}
}'

Anschließend das Bild vor der Anzeige auflösen:

const state = await bridge.request("state.get", {});
const file = await bridge.request("files.get", {
fileId: state.data.puzzleImageFileId
});
image.src = file.file.signedUrl;

files.capabilities

Prüfen Sie dies, bevor Sie die Upload-Steuerelemente anzeigen.

{
"action": "files.capabilities",
"payload": {}
}

Laufzeit files.upload

Der Upload von Dateien zur Laufzeit verändert die Sitzungsdaten der Erweiterung und ist daher kein Befehl zur Überbrückung von iFrames. Laden Sie Laufzeitdateien von Ihrem Backend mit einem anwendungsbezogenen Entwickler-API-Schlüssel und x-chastify-main-token hoch.

files.get

Eine signierte R2-URL anhand einer stabilen Datei-ID aktualisieren.

{
"action": "files.get",
"payload": {
"fileId": "file_record_id"
}
}

files.list

{
"action": "files.list",
"payload": {}
}

Die Laufzeitumgebung files.delete ist aus dem gleichen Grund wie der Upload ebenfalls nur im Backend verfügbar.

Sperraktionen

Zeit hinzufügen

Endpunkt: POST /api/extensions/sessions/:sessionId/action

Was es bewirkt:

  • Fügt je nach deltaSeconds Zeit zum Sperr-Countdown hinzu oder entfernt sie davon.

Wann verwenden?

  • Belohnungs-/Strafknöpfe.
  • Spielausgänge (Sieg bringt Zeit, Niederlage reduziert Zeit).

Beispiel:

{
"name": "add_time",
"params": 300 // +300 sec = +5 minutes
}

Feldnotizen:

  • Positiver Wert fügt Zeit hinzu.
  • Ein negativer Wert entfernt die Zeit (sofern dies von den Serverregeln zugelassen ist).

Einfrieren

Endpunkt: POST /api/extensions/sessions/:sessionId/action

Was es bewirkt:

  • Einfrieren blockiert den Spielfortschritt für eine bestimmte Zeit.

Wann verwenden?

  • Abklingmechaniken.
  • Belohnungs-Checkpoints.

Beispiel:

{
"name": "freeze",
"params": { "durationSeconds": 120 }
}

Sie können es auch ohne durationSeconds aufrufen:

{
"name": "freeze",
"params": {}
}

Feldnotizen:

  • durationSeconds ist optional.
  • Wird dieser Wert weggelassen, beträgt der aktuelle Standardwert 3600 Sekunden (1 Stunde).
  • Der zulässige Bereich liegt zwischen 60 und 86400 Sekunden.

Auftauen

Endpunkt: POST /api/extensions/sessions/:sessionId/action

Was es bewirkt:

  • Beendet den aktiven Einfriervorgang und stellt das normale Timerverhalten wieder her.

Wann verwenden?

  • Manuelle Überschreibung in Erweiterungs-Workflows.
  • „Einfrieren abbrechen“-Steuerelemente.

Beispiel:

{
"name": "unfreeze",
"params": {}
}

Pranger

Endpunkt: POST /api/extensions/sessions/:sessionId/action

Was es bewirkt:

  • Startet eine Prangerperiode für die aktive Sperrsitzung.

Wann verwenden?

  • Strafmechanismen nach fehlgeschlagenen Aufgaben/Herausforderungen.
  • Eskalationsabläufe, die die Interaktion mit Sperren vorübergehend einschränken.

Beispiel:

{
"name": "pillory",
"params": {
"durationSeconds": 600,
"reason": "Missed scheduled check-in"
}
}

Feldnotizen:

  • name muss pillory sein.
  • params.durationSeconds ist erforderlich.
  • params.reason ist optional.
  • Erfordert, dass der Pranger in der Sitzungskonfiguration aktiviert ist.

Schluss mit dem Pranger

Endpunkt: POST /api/extensions/sessions/:sessionId/action

Was es bewirkt:

  • Beendet die aktuell laufende Prangersitzung sofort.

Beispiel:

{
"name": "pillory.end",
"params": {}
}

Feldnotizen:

  • name muss pillory.end sein.
  • Fehler mit pillory_not_active, wenn sich das Schloss nicht im Pranger befindet.

Aufgabe zuweisen

Endpunkt: POST /api/extensions/sessions/:sessionId/action

Was es bewirkt:

  • Erstellt einen aktiven Aufgabenlauf für den Träger aus der Erweiterungslogik.
  • Kann einen bereits geöffneten Task-Lauf überschreiben.

Beispiel:

{
"name": "task.assign",
"params": {
"taskText": "Clean your room",
"points": 10,
"verificationRequired": true,
"durationSeconds": 1800
}
}

Feldnotizen:

  • taskText ist erforderlich.
  • points ist optional und serverseitig begrenzt.
  • verificationRequired ist standardmäßig false.
  • durationSeconds ist optional (0 bedeutet, dass kein Timer erforderlich ist).
  • Erfordert, dass das Aufgabenmodul auf dem Schloss aktiviert ist.

Task-Timer starten

Endpunkt: POST /api/extensions/sessions/:sessionId/action

Was es bewirkt:

  • Startet oder startet das Countdown-Fenster für die aktuell aktive zeitgesteuerte Aufgabe neu.

Beispiel:

{
"name": "task.start_timer",
"params": {}
}

Feldnotizen:

  • Erfordert einen aktiven Task-Lauf.
  • Schlägt fehl, wenn für die aktuelle Aufgabe keine Dauer konfiguriert ist.

Aufgabe abschließen

Endpunkt: POST /api/extensions/sessions/:sessionId/action

Was es bewirkt:

  • Markiert den aktuell ausgeführten Task als abgeschlossen oder fehlgeschlagen.

Beispiel (Erfolg):

{
"name": "task.complete",
"params": {
"successful": true
}
}

Beispiel (fehlgeschlagen):

{
"name": "task.complete",
"params": {
"successful": false,
"reason": "Did not finish in time"
}
}

Temporäre Öffnung auslösen

Endpunkt: POST /api/extensions/sessions/:sessionId/action

Was es bewirkt:

  • Startet ein temporäres Hygieneöffnungsfenster aus der Erweiterungslogik.

Beispiel:

{
"name": "hygienic_unlock.start",
"params": {
"durationSeconds": 900
}
}

Feldnotizen:

  • Erfordert die Aktivierung der hygienischen Öffnungsfunktion am Schloss.
  • Schlägt fehl, wenn eine Hygieneöffnung bereits im Gange ist.
  • durationSeconds ist optional; wird es weggelassen, wird der Standardwert für die Sperre verwendet.

Metadaten und Startseitenaktionen

metadata.patch

Was es bewirkt:

  • Speichert Erweiterungsmetadaten, die von der Sperrseiten-Benutzeroberfläche verwendet werden.
  • Unterstützt unlockBlockers und homeActions.
  • Unterstützt homeActions[].intent für das Deep-Link-Verhalten beim Öffnen der Erweiterung.

Wann verwenden?

  • Setzen Sie die Sperr- und Entsperrbedingungen Ihrer Erweiterung durch.
  • Fügen Sie auf der Sperrseite Schnellaktionen hinzu, die Ihre Erweiterung mit einer Absicht öffnen.
  • Leiten Sie Benutzer direkt zu einem bestimmten Bildschirm/Workflow weiter, wenn sie auf eine Aktion auf der Startseite klicken.

Endpunkt:

PATCH /api/extensions/sessions/:sessionId/metadata
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH

Beispieltext:

{
"unlockBlockers": ["Finish extension task"],
"homeActions": [
{
"slug": "tasks",
"title": "Tasks",
"description": "Open tasks panel",
"intent": {
"type": "open_panel",
"title": "Tasks",
"message": "Open tasks panel",
"payload": {
"panel": "regular-actions"
}
}
}
]
}

Feldnotizen:

  • unlockBlockers: Liste der aktiven Sperr- und Entsperrungsblocker Ihrer Nebenstelle.
  • Sie können mehrere Blocker gleichzeitig einbeziehen (bis zu 25), einen pro nicht erfüllter Bedingung.
  • Die Entsperrung bleibt so lange blockiert, wie in den aktivierten Erweiterungen eine Blockierung aktiv ist.
  • Chastify fasst Blocker aller Nebenstellen für die Sperrsitzung zusammen.
  • Ihre Erweiterung sollte nur ihre eigenen Blocker in ihren eigenen Metadaten hinzufügen/entfernen.
  • Löschen Sie keine Blocker anderer Erweiterungen; leeren Sie Ihr Array nur dann, wenn Ihre eigenen Bedingungen erfüllt sind.
  • homeActions: Schnellaktionsschaltflächen werden in der Sperransicht angezeigt.
  • homeActions[].slug: Stabile ID für Ihre Aktion.
  • homeActions[].title: Benutzerseitiges Etikett.
  • homeActions[].description: optionaler Hilfetext.
  • homeActions[].intent: Optionale Deep-Link-Anweisung, die beim Öffnen an Ihre Erweiterung übergeben wird.
  • In der Benutzeroberfläche der Erweiterungskarte werden diese Aktionen als Menü/Liste nach Aktionstitel angezeigt (intern nach Slug kategorisiert).
  • Wenn ein Benutzer darauf klickt, öffnet Chastify die Erweiterung und übergibt:
    • homeActionSlug
    • homeAction (ausgewähltes Aktionsobjekt)
    • intent (normalisiertes Intent-Objekt) damit Ihre Erweiterung beim Laden sofort zur richtigen Ansicht/Aktion weiterleiten kann.

Intents: Beispiel für eine Entwickler-App

Verwenden Sie dieses Muster in Ihrer Erweiterungs-App, um beim Laden auf Menüklick-Intents zu reagieren.

import { useEffect, useRef } from "react";
import { parseHashPayload, type IframeHashPayload } from "../lib/ChastifyBridge";

export function useHomeActionIntent(
payload: IframeHashPayload,
routeToPanel: (panel: string) => void,
showToast: (message: string) => void,
) {
const handledRef = useRef<string>("");

useEffect(() => {
const homeActionSlug = payload?.homeActionSlug ?? null;
if (!homeActionSlug) return;

// Prevent duplicate handling if component re-renders.
const key = `${payload.lockId || "lock"}:${homeActionSlug}`;
if (handledRef.current === key) return;
handledRef.current = key;

const intent = payload?.intent ?? payload?.homeAction?.intent ?? null;
if (!intent) return;

if (intent.type === "open_panel") {
const panel = String(intent.payload?.panel || "");
if (panel) routeToPanel(panel);
return;
}

if (intent.message) {
showToast(String(intent.message));
}
}, [payload, routeToPanel, showToast]);
}

// Example app bootstrap
const payload = parseHashPayload();
if (!payload) throw new Error("Missing iframe hash payload");

Was dieses Beispiel bewirkt:

  • Liest homeActionSlug + intent aus der Hash-Nutzlast des iFrames.
  • Verarbeitet jeden Klick nur einmal pro Sperr-/Aktionssegment.
  • Leitet zu einem Panel weiter, wenn die Absicht open_panel ist.
  • Bei benutzerdefinierten Absichtstypen wird stattdessen eine Absichtsnachricht angezeigt.

Gerätebefehl

device.command

Was es bewirkt:

  • Sendet einen Gerätesteuerungsbefehl (sofern für diese Sitzung/dieses Gerät unterstützt).
  • Ermöglicht es Ihrer Verlängerung, standardisierte Schock-/Vibrationsaktionen über Chastify auszulösen.

Wann verwenden?

  • Auslösen unterstützter Schock-/Vibrationsbefehle aus der Erweiterungslogik.
  • Entwicklung interaktiver Erweiterungsfunktionen (Spiele, Strafen, Belohnungen, Routinen).

Endpunkt:

POST /api/extensions/sessions/:sessionId/device-command
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH

Beispieltext:

{
"command": "shock.start",
"params": {
"durationSeconds": 30,
"intensityPct": 50
}
}

Allgemeine Befehle:

  • shock.start mit Parametern: { durationSeconds, intensityPct, message? }
  • shock.stop
  • vibration.start mit Parametern: { durationSeconds, intensityPct, frequencyPct?, message? }
  • vibration.stop
  • all.stop
  • shock.random.set mit Parametern: { enabled, minIntensityPct?, maxIntensityPct?, message? } (nur Lockink AA-A1012)
  • shock.berserk.set mit Parametern: { enabled, message? } (nur Lockink AA-A1012)

Empfohlener Ablauf:

  1. Beim Laden session.get aufrufen.
  2. Gerätefunktionen von deviceControl.supportedCommands auslesen.
  3. Nur Steuerelemente für unterstützte Befehle rendern.
  4. Senden Sie device.command mit validierten Werten.
  5. Aktualisieren oder vertrauen Sie den Antwortflags active für den Live-UI-Status.

Parameterhinweise:

  • durationSeconds: Server begrenzt auf sichere Grenzwerte (aktuelle Richtlinie: Maximalwert 300 Sekunden).
  • intensityPct: erwarteter Prozentwert (Eingabe im Stil 1-100, serverseitig begrenzt).
  • frequencyPct: erwarteter Prozentwert (1-100) für Schwingungsströme (eingespannt serverseitig).
  • Für den Zufallsmodus muss minIntensityPct <= maxIntensityPct gelten.

Beispiel für ein sichereres UI-Muster:

// 1) Get capabilities first
const session = await bridge.request("session.get", {});
const supported = new Set(session?.deviceControl?.supportedCommands ?? []);

// 2) Only call command if supported
if (supported.has("shock.start")) {
await bridge.request("device.command", {
command: "shock.start",
params: { durationSeconds: 30, intensityPct: 50 }
});
}

Wichtig:

  • Rufen Sie zuerst session.get auf und lesen Sie die Liste der unterstützten Befehle.
  • Es werden nur Steuerelemente für Befehle angezeigt, die in der aktuellen Sitzung unterstützt werden.
  • Benutzereingaben vor dem Senden von Befehlsparametern prüfen.
  • Für device.command wird die Schreibberechtigung (locks:write) für die Erweiterungssitzung benötigt.
  • Bridge-/Serverfehler ordnungsgemäß behandeln (insufficient_scope, nicht unterstützter Befehl, Validierungsfehler).

Verlängerungsanforderungen

Die Erweiterungsanforderungen ermöglichen es einer Erweiterung, wiederkehrende Abschlussregeln zu definieren, die in der Fortschrittsanzeige des Schlosses angezeigt werden und eine Strafe verhängen können, wenn der Träger ein Zeitfenster verpasst.

Verwenden Sie dies für serverseitig vertrauenswürdige Erweiterungsaktivitäten wie zum Beispiel:

  • Löse täglich 1 Rätsel.
  • Führen Sie alle zwei Tage drei Check-ins durch.
  • Erledigen Sie 5 Verlängerungsmaßnahmen pro Woche.

Konfigurationsform

Die Anforderung ist in der Erweiterungssitzungskonfiguration unter extensionRequirements gespeichert:

{
"extensionRequirements": {
"enabled": true,
"metric": "completion",
"requiredCount": 1,
"cadence": {
"every": 1,
"unit": "day"
},
"punishment": {
"type": "add_time",
"seconds": 900,
"reason": "Missed extension requirement"
}
}
}

Felder:

  • enabled: Schaltet die wiederkehrende Anforderung ein/aus.
  • metric: Name des stabilen Zählers. Verwenden Sie einfache Namen wie completion, win oder verification.
  • requiredCount: Wie viele vertrauenswürdige Ereignisse müssen innerhalb des Fensters stattfinden?
  • cadence.every: Intervallgröße.
  • cadence.unit: day oder week.
  • Die Fensterzeitzone wird über Chastify aus dem vom Träger konfigurierten User.timezone aufgelöst. Die Konfiguration der Erweiterung sollte keine fest codierte Zeitzone enthalten.
  • punishment.type: none, add_time, freeze oder pillory.
  • punishment.seconds: Strafdauer für add_time, freeze oder pillory.
  • punishment.reason: optionaler Prüf-/Debug-Grund.

Fortschrittsmodell

Der Fortschritt der Anforderungen ist ein vertrauenswürdiger serverseitiger Zustand, kein iframe-lokaler Zustand.

Verwenden Sie state.patch / state.put nicht, um eine Anforderung als abgeschlossen zu markieren. Diese Aktionen sind allgemeine Statusänderungen im Backend und keine APIs zur Fortschrittsverfolgung von Anforderungen. Der Anforderungsfortschritt darf erst protokolliert werden, nachdem der Server das entsprechende Ereignis validiert hat.

Zum Beispiel:

  • Eine Puzzle-Erweiterung sollte den Fortschritt erst dann aufzeichnen, nachdem der Server den signierten Puzzle-Lauf und den Abschlussstatus validiert hat.
  • Eine Verifizierungserweiterung sollte den Fortschritt erst dann aufzeichnen, nachdem der Server den eingereichten Nachweis akzeptiert hat.
  • Eine Spielerweiterung sollte den Spielfortschritt erst dann aufzeichnen, nachdem das Backend das Spielergebnis oder ein vertrauenswürdiges Abschlussereignis validiert hat.

Laufzeitverhalten

Im konfigurierten Zustand:

  • Das Sperr-Dashboard kann die Anforderungen im heutigen Fortschritt anzeigen.
  • Chastify verfolgt den Fortschritt pro Erweiterungssitzung und pro Kadenzfenster.
  • Der geplante Anforderungsjob wertet abgeschlossene Fenster aus und wendet die konfigurierte Strafe einmal pro versäumtem Fenster an.
  • Strafen sind pro Fenster idempotent, sodass Wiederholungsversuche keine doppelten Strafen verursachen.
  1. Konfigurieren Sie extensionRequirements in der Benutzeroberfläche für die Einrichtung/Konfiguration der Erweiterung.
  2. Beim Start zur Laufzeit wird session.get aufgerufen und die aktive Konfiguration gelesen.
  3. Schließen Sie die Erweiterungsaktivität in der Benutzeroberfläche ab.
  4. Sende die Abschlussmeldung an eine vertrauenswürdige Backend-Route für diese Erweiterung.
  5. Das Backend soll das Ereignis validieren und den Fortschritt der Anforderung protokollieren.
  6. Nach Abschluss die lokale Benutzeroberfläche mit session.get / state.get aktualisieren.

Wichtig:

  • Behandeln Sie state.* ausschließlich als Speicherbereich der Erweiterung. Verwenden Sie dedizierte, vertrauenswürdige APIs für Fortschritt, Versuche, Belohnungen und Strafen.
  • Vertrauen Sie nicht ausschließlich clientseitigen Abschlussflags für Anforderungen.
  • Die Namen metric sollten unverändert bleiben; bei einer Änderung der Metrik wird in einem anderen Bucket gezählt.
  • Chastify verwendet die vom Benutzer konfigurierte Zeitzone für die Kadenzfenster. Falls keine Zeitzone für den Benutzer verfügbar ist, greift der Server auf UTC zurück.
  • Strafen sollten begrenzt und in den Prüfprotokollen nachvollziehbar sein.
  1. Beim Start session.get aufrufen.
  2. Status mit state.get lesen.
  3. Führen Sie bei Bedarf Statusänderungen von Ihrem Backend mit PUT/PATCH /state durch.
  4. Sperr-/Geräteaktionen werden nur ausgeführt, wenn sie unterstützt werden und in der Benutzeroberfläche sichtbar sind.
  5. Bei anforderungsbasierten Aktivitäten wird der Abschluss an eine vertrauenswürdige Backend-Route gemeldet.
  6. Lokale Ansicht nach wichtigen Schreibvorgängen/Aktionen aktualisieren.