API Examples
Use these actions inside iframe bridge requests (action + payload).
Request and Response Format
Every action below is sent inside this bridge request envelope:
{
"type": "chastify:ext:req", // required
"v": 1, // protocol version
"id": "req-123", // your unique request id
"nonce": "from-iframe-hash",
"action": "session.get",
"payload": {}
}
And you receive:
{
"type": "chastify:ext:resp",
"v": 1,
"id": "req-123", // same id you sent
"ok": true,
"data": {}
}
If ok is false, check error.code and error.message.
Session and Context
session.get
Use this first in almost every extension flow.
What it does:
- Verifies your bridge connection is working.
- Returns session context (lock state, role, extension config, capabilities).
- Returns device capability information used by
device.command.
What it is used for:
- Bootstrapping your UI.
- Enabling/disabling features based on role and permissions.
- Checking supported device commands before rendering device controls.
Example action payload:
{
"action": "session.get",
"payload": {}
}
Example session.get response excerpt (runtime lock data):
{
"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
}
}
}
Privacy notes:
wearerLastSeenTimestamp/keyholderLastSeenTimestampare returned only if that user hasshowOnlineStatus !== false.- If online status visibility is disabled, these fields are
null.
Notifications
notifications.custom
Use this to send a custom extension notification to wearer, keyholder, or both.
Example action payload:
{
"action": "notifications.custom",
"payload": {
"title": "Extension Reminder",
"message": "Your next challenge is ready.",
"showPageOverlay": false,
"target": "both"
}
}
Notes:
showPageOverlaydefaults tofalse.targetdefaults towearer.- API creates notification type
extension_app_message.
Extension State
Extension state is your extension-owned JSON data for the current lock session. You can use it like a lightweight per-session database when you do not run your own backend, which is especially useful for static/serverless extension hosting such as Cloudflare Pages.
state.put
What it does:
- Replaces the entire state object with the new
dataobject.
When to use:
- Initial save.
- Full overwrite when you already have the complete new state.
Example:
{
"action": "state.put",
"payload": {
"data": {
"counter": 1,
"notes": "first test"
}
}
}
Field notes:
payload.data: any valid JSON value/object your extension needs.- Avoid Mongo-unsafe key names (
$prefix or keys containing.).
state.patch
What it does:
- Applies a JSON merge patch to existing state.
- Only changed keys need to be sent.
When to use:
- Incremental updates from user interactions.
- Updating one field without resending everything.
Example:
{
"action": "state.patch",
"payload": {
"data": {
"counter": 2
}
}
}
state.get
What it does:
- Reads the current extension state.
When to use:
- On iframe load.
- After writes, if you want to re-sync local UI.
Example:
{
"action": "state.get",
"payload": {}
}
Lock Actions
Add time
Action: lock.applyTime
What it does:
- Adds or removes time from the lock countdown depending on
deltaSeconds.
When to use:
- Reward/penalty buttons.
- Game outcomes (win adds time, lose removes time).
Example:
{
"action": "lock.applyTime",
"payload": {
"deltaSeconds": 300 // +300 sec = +5 minutes
}
}
Field notes:
- Positive value adds time.
- Negative value removes time (if permitted by server rules).
Freeze
Action: lock.freeze
What it does:
- Freezes lock progression for a duration.
When to use:
- Cooldown mechanics.
- Reward checkpoints.
Example:
{
"action": "lock.freeze",
"payload": {
"durationSeconds": 120
}
}
You can also call it without durationSeconds:
{
"action": "lock.freeze",
"payload": {}
}
Field notes:
durationSecondsis optional.- If omitted, the current default is
3600seconds (1 hour). - Accepted range is
60to86400seconds.
Unfreeze
Action: lock.unfreeze
What it does:
- Ends active freeze and resumes normal timer behavior.
When to use:
- Manual override in extension workflows.
- “Cancel freeze” controls.
Example:
{
"action": "lock.unfreeze",
"payload": {}
}
Pillory
Action: lock.action
What it does:
- Starts a pillory period for the active lock session.
When to use:
- Penalty mechanics after failed tasks/challenges.
- Escalation flows that temporarily restrict lock interaction.
Example:
{
"action": "lock.action",
"payload": {
"name": "pillory",
"params": {
"durationSeconds": 600,
"reason": "Missed scheduled check-in"
}
}
}
Field notes:
namemust bepillory.params.durationSecondsis required.params.reasonis optional.- Requires pillory to be enabled in the session configuration.
End pillory
Action: lock.action
What it does:
- Ends the current active pillory session immediately.
Example:
{
"action": "lock.action",
"payload": {
"name": "pillory.end",
"params": {}
}
}
Field notes:
namemust bepillory.end.- Fails with
pillory_not_activeif the lock is not currently in pillory.
Assign task
Action: task.assign
What it does:
- Creates an active task run for the wearer from extension logic.
- Can override an already-open task run.
Example:
{
"action": "task.assign",
"payload": {
"taskText": "Clean your room",
"points": 10,
"verificationRequired": true,
"durationSeconds": 1800
}
}
Field notes:
taskTextis required.pointsis optional and clamped server-side.verificationRequireddefaults tofalse.durationSecondsis optional (0means no timer requirement).- Requires Tasks module enabled on the lock.
Start task timer
Action: task.start_timer
What it does:
- Starts or restarts the countdown window for the currently active timed task.
Example:
{
"action": "task.start_timer",
"payload": {}
}
Field notes:
- Requires an active task run.
- Fails if the current task has no duration configured.
Complete task
Action: task.complete
What it does:
- Marks the active task run as completed or failed.
Example (success):
{
"action": "task.complete",
"payload": {
"successful": true
}
}
Example (fail):
{
"action": "task.complete",
"payload": {
"successful": false,
"reason": "Did not finish in time"
}
}
Trigger temporary opening
Action: hygienic_unlock.start
What it does:
- Starts a temporary hygiene opening window from extension logic.
Example:
{
"action": "hygienic_unlock.start",
"payload": {
"durationSeconds": 900
}
}
Field notes:
- Requires Hygienic Opening enabled on the lock.
- Fails if a hygiene opening is already in progress.
durationSecondsis optional; lock default is used when omitted.
Metadata and Home Actions
metadata.patch
What it does:
- Stores extension metadata used by lock-page UI.
- Supports
unlockBlockersandhomeActions. - Supports
homeActions[].intentfor deep-link behavior when extension opens.
When to use:
- Enforce lock-session unlock conditions owned by your extension.
- Add quick actions on lock page that open your extension with intent.
- Route users directly to a specific screen/workflow when they click a home action.
Example:
{
"action": "metadata.patch",
"payload": {
"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"
}
}
}
]
}
}
Field notes:
unlockBlockers: list of active lock-session unlock blockers from your extension.- You can include multiple blockers at once (up to 25), one per unmet condition.
- Unlock remains blocked while any blocker exists across enabled extensions.
- Chastify aggregates blockers from all extensions for the lock session.
- Your extension should only add/remove its own blockers in its own metadata.
- Do not clear blockers from other extensions; clear your array only when your own conditions are satisfied.
homeActions: quick action buttons displayed in lock experience.homeActions[].slug: stable id for your action.homeActions[].title: user-facing label.homeActions[].description: optional helper text.homeActions[].intent: optional deep-link instruction passed to your extension when opened.- In the extension card UI, these actions are shown as a menu/list by action title (internally keyed by slug).
- When a user clicks one, Chastify opens the extension and passes:
homeActionSlughomeAction(selected action object)intent(normalized intent object) so your extension can immediately route to the correct view/action on load.
Intents: developer app example
Use this pattern in your extension app to react to menu-click intents on load.
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");
What this example does:
- Reads
homeActionSlug+intentfrom iframe hash payload. - Handles each click only once per lock/action slug.
- Routes to a panel when intent is
open_panel. - Falls back to showing an intent message for custom intent types.
Device Command
device.command
What it does:
- Sends a device control command (if supported for that session/device).
- Lets your extension trigger standardized shock/vibration actions through Chastify.
When to use:
- Triggering supported shock/vibration commands from extension logic.
- Building interactive extension features (games, punishments, rewards, routines).
Example:
{
"action": "device.command",
"payload": {
"command": "shock.start",
"params": {
"durationSeconds": 30,
"intensityPct": 50
}
}
}
Common commands:
shock.startwith params:{ durationSeconds, intensityPct, message? }shock.stopvibration.startwith params:{ durationSeconds, intensityPct, frequencyPct?, message? }vibration.stopall.stopshock.random.setwith params:{ enabled, minIntensityPct?, maxIntensityPct?, message? }(Lockink AA-A1012 only)shock.berserk.setwith params:{ enabled, message? }(Lockink AA-A1012 only)
Recommended flow:
- Call
session.geton load. - Read device capabilities from
deviceControl.supportedCommands. - Render only controls for supported commands.
- Send
device.commandwith validated values. - Refresh or trust response
activeflags for live UI state.
Parameter guidance:
durationSeconds: server clamps to safe limits (current policy max is 300s).intensityPct: expected percentage value (1-100style input, clamped server-side).frequencyPct: expected percentage value (1-100) for vibration flows (clamped server-side).- For random mode, ensure
minIntensityPct <= maxIntensityPct.
Safer UI pattern example:
// 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 }
});
}
Important:
- Call
session.getfirst and read supported commands. - Only show controls for commands that are supported in current session.
- Validate user inputs before sending command params.
device.commandrequires write permission (locks:write) for the extension session.- Handle bridge/server errors gracefully (
insufficient_scope, unsupported command, validation errors).
Recommended Action Order
- Call
session.geton startup. - Read state with
state.get. - Perform writes with
state.patch/state.put. - Run lock/device actions only when supported and visible in UI.
- Refresh local view after important writes/actions.