Skip to main content

Extension API Features

Use this page when you already understand the basic extension model and want to build actual lock behavior: required actions, unlock blockers, regularity, rewards, and punishments.

caution

Browser iframe code is not trusted.

Your iframe can render UI and read safe session context. Anything that writes session state, rewards the wearer, removes time, clears an unlock blocker, records trusted requirement progress, starts or fails an attempt timer, or applies punishment must be verified by your backend.

Feature Map

FeatureWhat it doesWhere it runs
Unlock blockersPrevent unlock until your extension clears a blocker or records enough trusted progressLock-start config or backend metadata/progress calls
Home actionsAdds a visible action on the lock page, such as "Play challenge"Lock-start config or backend metadata patches
Regular actionsAllows a limited number of wearer actions per intervalChastify tracks counters; your backend submits trusted actions
Required progressTracks daily or weekly completion requirementsYour backend records progress; Chastify scheduler checks missed windows
RewardsRemoves time, starts hygiene opening, or sends notification after verified successYour backend calls privileged endpoints
PunishmentsAdds time, freezes, assigns a task, starts pillory, or sends notification after verified failureYour backend calls privileged endpoints

Required Backend Headers

Privileged extension endpoints require both headers:

Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH

The mainToken identifies the opened extension session. The Developer API key proves the request came from your backend.

Unlock Blockers

Use unlock blockers when the wearer must finish something before unlocking. Examples:

  • Win 3 games.
  • Complete today's workout proof.
  • Finish one verification puzzle.
  • Complete all required extension actions for the current window.

There are two supported patterns.

Progress-based blockers

Use initialMetadata.unlockRequirements when the lock should be blocked as soon as the lock starts or a shared template is accepted. Chastify initializes the extension session state from this generic config, so the blocker exists even before the wearer opens your extension for the first time.

{
"initialMetadata": {
"unlockRequirements": [
{
"metric": "memory_win",
"requiredCount": 3,
"blocker": "Complete 3 memory challenge wins to unlock"
}
],
"homeActions": [
{
"slug": "play-memory-challenge",
"title": "Play memory challenge",
"description": "Complete the extension challenge to satisfy the unlock requirement.",
"icon": "gamepad-2",
"badge": "Required"
}
]
}
}

After your backend verifies a trusted completion, record progress for the same metric:

curl -X POST "https://chastify.net/api/extensions/sessions/$SESSION_ID/requirements/progress" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"metric": "memory_win",
"amount": 1
}'

Chastify stores this as unlock progress and automatically treats the requirement as satisfied when progress reaches requiredCount. Lock cards can show progress such as 1 of 3 complete.

Use stable metric names. One metric represents one active unlock requirement for that extension session.

Attempt time limits

Use attemptLimit when a trusted attempt must expire if it is not completed in time. Chastify stores the active attempt under data.attemptLimits.<metric> and can mark it failed after the deadline. This state is Chastify-managed; start and fail attempts through the attempt endpoints instead of writing data.attemptLimits yourself.

{
"attemptLimit": {
"enabled": true,
"metric": "memory_win",
"durationSeconds": 300,
"startPolicy": "activity"
}
}

startPolicy can be activity when the countdown starts as the wearer starts playing, or availability when the countdown starts as soon as the action is available for the lock.

Start an attempt from your backend:

curl -X POST "https://chastify.net/api/extensions/sessions/$SESSION_ID/attempts/start" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"metric": "memory_win",
"attemptId": "memory-run-123"
}'

When your backend checks the attempt, ask Chastify to fail it if the deadline has passed:

curl -X POST "https://chastify.net/api/extensions/sessions/$SESSION_ID/attempts/fail-expired" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"metric": "memory_win",
"attemptId": "memory-run-123"
}'

If failed attempts should consume a cooldown slot, also submit a regular action for the failed attempt.

Manual blockers

Use unlockBlockers when your backend needs to decide manually whether unlocking is blocked. Patch metadata from your backend:

curl -X PATCH "https://chastify.net/api/extensions/sessions/$SESSION_ID/metadata" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"unlockBlockers": [
"Complete 3 memory challenge wins to unlock"
],
"homeActions": [
{
"slug": "play-memory-challenge",
"title": "Play memory challenge",
"description": "Complete the extension challenge to satisfy the unlock requirement.",
"icon": "gamepad-2",
"badge": "Required",
"intent": {
"type": "play",
"title": "Memory challenge",
"message": "Complete the required challenge."
}
}
]
}'

Clear the blocker after your backend verifies completion:

curl -X PATCH "https://chastify.net/api/extensions/sessions/$SESSION_ID/metadata" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"unlockBlockers": [],
"homeActions": [
{
"slug": "play-memory-challenge",
"title": "Memory challenge complete",
"description": "The extension unlock requirement has been completed.",
"icon": "check-circle",
"badge": "Complete"
}
]
}'
note

unlockBlockers is manual metadata. It tells Chastify that unlocking is blocked until your backend removes the blocker. initialMetadata.unlockRequirements is progress-based metadata. Chastify can evaluate it automatically from trusted progress recorded for the matching metric.

Regularity Feature

Regular actions are useful when the wearer is allowed to do something at a fixed cadence: once per hour, four times per day, one submission per week, and so on.

You can configure regular actions when the lock extension is created, or update the running session from your backend:

curl -X PATCH "https://chastify.net/api/extensions/sessions/$SESSION_ID/regular-actions/config" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mode": "cumulative",
"regularitySeconds": 86400,
"maxActions": 4
}'

Modes:

  • unlimited: actions are always available.
  • non_cumulative: one window becomes available at a time; unused actions do not stack.
  • cumulative: actions accrue up to maxActions.

Read current availability:

curl "https://chastify.net/api/extensions/sessions/$SESSION_ID/regular-actions" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN"

Submit a trusted regular action:

curl -X POST "https://chastify.net/api/extensions/sessions/$SESSION_ID/regular-actions" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"payload": {
"kind": "checkin",
"score": 92
}
}'

Use regular actions when the main rule is "how often can this action happen?".

Custom Required Actions

Use required progress when the main rule is "how many completions are required per day or week?".

The requirement config lives in the installed extension session config under extensionRequirements:

{
"extensionRequirements": {
"enabled": true,
"metric": "completion",
"requiredCount": 4,
"cadence": {
"every": 1,
"unit": "day",
"timezone": "UTC"
},
"punishment": {
"type": "add_time",
"seconds": 1800,
"reason": "Daily extension requirement missed"
}
}
}

Supported cadence units:

  • day
  • week

Supported missed-window punishment types:

  • none
  • add_time
  • freeze
  • pillory

Record trusted progress only after your backend verifies the action:

curl -X POST "https://chastify.net/api/extensions/sessions/$SESSION_ID/requirements/progress" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"metric": "completion",
"amount": 1,
"occurredAt": "2026-05-31T18:42:41.715Z"
}'

Chastify's scheduler evaluates completed windows and applies the configured missed-window punishment if the completed count is below requiredCount.

tip

Use both features together when needed:

  • regular-actions limits how often a wearer can submit.
  • requirements/progress records whether enough verified completions happened for the day or week.
  • metadata.unlockBlockers blocks unlock until the current requirement is satisfied.

Rewards

Rewards are privileged lock actions. Call them from your backend after a verified success.

Remove time:

curl -X POST "https://chastify.net/api/extensions/sessions/$SESSION_ID/action" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "remove_time",
"params": 600
}'

Start a hygienic opening:

curl -X POST "https://chastify.net/api/extensions/sessions/$SESSION_ID/action" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "hygienic_unlock.start",
"params": {
"durationSeconds": 900
}
}'

Send a custom notification:

curl -X POST "https://chastify.net/api/extensions/sessions/$SESSION_ID/notifications/custom" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Challenge complete",
"message": "Your extension reward was applied.",
"target": "wearer"
}'

Punishments

Punishments are also privileged lock actions. Apply them only after your backend verifies a failure or missed requirement.

Add time:

curl -X POST "https://chastify.net/api/extensions/sessions/$SESSION_ID/action" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "add_time",
"params": 1800
}'

Freeze:

curl -X POST "https://chastify.net/api/extensions/sessions/$SESSION_ID/action" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "freeze",
"params": {
"durationSeconds": 3600
}
}'

Assign a task:

curl -X POST "https://chastify.net/api/extensions/sessions/$SESSION_ID/action" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "task.assign",
"params": {
"actor": "extension",
"taskText": "Write a short reflection about the missed requirement.",
"points": 5,
"durationMinutes": 30
}
}'

Start pillory:

curl -X POST "https://chastify.net/api/extensions/sessions/$SESSION_ID/action" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "pillory",
"params": {
"durationSeconds": 3600,
"reason": "Extension requirement missed"
}
}'

For games, routines, and verification challenges:

  1. Iframe opens and reads the launch hash.
  2. Iframe asks your backend to create a challenge using mainToken.
  3. Backend exchanges/uses mainToken with its app-scoped Developer API key.
  4. Iframe submits answers to your backend.
  5. Backend verifies the result.
  6. Backend records trusted progress with requirements/progress.
  7. Backend applies reward or punishment with /action.
  8. Backend patches metadata.unlockBlockers and metadata.homeActions.
  9. Iframe reads safe state/metadata and shows the result.

This keeps the extension UI responsive while keeping trusted lock state on the server.