The decision layer for AI agents. Your agent asks, a person approves or denies on their phone.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Bearer <Pushary API key> (format pk_xxx.sk_xxx, from pushary.com)
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Request an unresolved decision or missing input from the user through Pushary. Supports confirm, select and input questions. Delivery and waiting follow the account policy; a call waits at most 55 seconds and may return immediately. Returns the question state, answer when available, delivery information and handoff fields. answerUrl is optional. A pending question remains answerable after a poll ends. Sends a real question; it does not grant permission for other actions.
The complete question, up to 8000 characters. Preserve every point. Notifications show a short preview; the answer interface shows the full question.
Set to "test" from a test suite. The question is stored and returned as pending, and nothing is delivered to a phone, browser or Slack. The X-Pushary-Env: test header does the same for every call on the connection.
Deliver only to subscribers that have any of these tags.
Question type: confirm renders yes/no buttons, select renders the options list, input renders a free-text field.
true (default) blocks until the user answers or the timeout fires. Set false to return immediately with a pending correlationId and poll it yourself via wait_for_answer.
The concrete operation about to happen, one line. Shown as the Action line. Cut to 500 chars.
The user's stated task (from their last prompt), one line. Shown as the Intent line so the user can see why the agent stopped. Cut to 500 chars.
The single gating reason the agent stopped, one line. Shown as the Blocker line. Cut to 500 chars.
One or two sentences about what the agent is working on, shown above the question so the user can decide without opening the terminal.
The 2 to 6 choices for a select question. Required when type is "select", ignored otherwise. The answered value is the chosen option string.
Stable repository identity for the working directory, e.g. "github.com/acme/api". Lets an approval routing rule scoped to one repository avoid governing another. Optional; omit it and only workspace-wide routing rules apply.
The tool this approval is for (e.g. "Bash"), so the user can choose to always-allow it.
MACHINE-POPULATED. Exact absolute Write file_path from the runtime, for diagnostic correlation only. Models must omit it.
Name of the agent asking, format "{Agent} - {project}" (e.g. "Claude Code - myproject"). Shown in the notification title so the user knows which session needs them. Falls back to the MCP client name if omitted. Cut to 100 chars.
Stable machine id of the asking agent, so two machines never collapse into one session.
ONE question, in the richer Claude-compatible shape: a header, per-option descriptions, multiSelect, and an optional write-in. Exactly one keeps already-installed clients answerable; asking several means several calls. Runtime-populated; ordinary callers should omit it and use question/type/options.
MACHINE-POPULATED. The CALLER's own identifier for one logical invocation, used only when the runtime supplies no toolUseId. Mint it once, outside your retry loop, and send the same value on every attempt, so three retries of one ask become one decision. Do NOT derive it from the question text or reuse it across two deliberate asks: both collapse a real second question into the first one's answer. If you are a model deciding to call this tool, omit this field.
Set ONLY when this approval exists because the path falls outside the scope the user ratified via propose_scope. Approving then widens the run scope to include this exact path, so the user is not asked again for the same area.
Opaque per-session id of the asking agent, so parallel sessions are attributed separately.
How long this call blocks, in milliseconds (max 55000). Defaults to the site policy timeout. Ordinary questions stay open until answered or cancelled. A timeout ends this poll only; follow up with wait_for_answer.
MACHINE-POPULATED. The agent RUNTIME's own identifier for the tool call this approval gates, forwarded verbatim by a hook that received it. Do NOT invent, guess, derive, or reuse a value: two different questions sent under the same id collapse into one, and the second one never reaches a human. If you are a model deciding to call this tool, omit this field.
The diff (Edit/Write) or full command (Bash/apply_patch), secret-redacted and size-capped. Rendered as a collapsible detail block; never used as the push body.
Compact target of the tool call (e.g. the command head "git push" for Bash, or a file extension like ".ts" for Edit/Write). Used to mine policy suggestions.
MACHINE-POPULATED. When the agent hook stops waiting live and hands control back to the terminal. The question may remain answerable after this time. Ordinary callers should omit it.
Webhook URL that receives a POST with the answer when the user responds, signed with the X-Pushary-Signature header. Useful when the agent process may exit before the answer arrives.
Deliver only to subscribers matching these external IDs.
MACHINE-POPULATED. true from a hook that ends a paused approval exactly as the same approval unanswered after its full wait. Any other value is ignored. If you are a model deciding to call this tool, omit this field.
Hint text shown inside the free-text field for input questions
MACHINE-POPULATED. Ownership supplied by the supported personal Codex bridge. Models must omit this field.
Deliver only to these subscriber IDs. Omit all targeting fields to reach every connected device.
MACHINE-POPULATED. The agent runtime's own permission or approval mode for the tool call this approval gates, recorded with the decision. Any other value is ignored. If you are a model deciding to call this tool, omit this field.
MACHINE-POPULATED. Set by a Pushary hook whose engine applies a session tool grant with its risky-command ceiling, so the grant is only offered where it takes effect. If you are a model deciding to call this tool, omit this field.
MACHINE-POPULATED. false from a hook that cannot show its own approval prompt for this call, so the phone is asked every time and never spared for presence at the keyboard. Any other value is ignored. If you are a model deciding to call this tool, omit this field.
The server marks this tool as destructive. Running it may delete or overwrite data on the server or in any account you connected with your headers.
Request an unresolved decision or missing input from the user through Pushary. Supports confirm, select and input questions. Delivery and waiting follow the account policy; a call waits at most 55 seconds and may return immediately. Returns the question state, answer when available, delivery information and handoff fields. answerUrl is optional. A pending question remains answerable after a poll ends. Sends a real question; it does not grant permission for other actions.
| Argument | Type | Description |
|---|---|---|
| env | string | Set to "test" from a test suite. The question is stored and returned as pending, and nothing is delivered to a phone, browser or Slack. The X-Pushary-Env: test header does the same for every call on the connection. |
| tags | array | Deliver only to subscribers that have any of these tags. |
| type | string | Question type: confirm renders yes/no buttons, select renders the options list, input renders a free-text field. |
| wait | boolean | true (default) blocks until the user answers or the timeout fires. Set false to return immediately with a pending correlationId and poll it yourself via wait_for_answer. |
| action | string | The concrete operation about to happen, one line. Shown as the Action line. Cut to 500 chars. |
| intent | string | The user's stated task (from their last prompt), one line. Shown as the Intent line so the user can see why the agent stopped. Cut to 500 chars. |
| blocker | string | The single gating reason the agent stopped, one line. Shown as the Blocker line. Cut to 500 chars. |
| context | string | One or two sentences about what the agent is working on, shown above the question so the user can decide without opening the terminal. |
| options | array | The 2 to 6 choices for a select question. Required when type is "select", ignored otherwise. The answered value is the chosen option string. |
| repoKey | string | Stable repository identity for the working directory, e.g. "github.com/acme/api". Lets an approval routing rule scoped to one repository avoid governing another. Optional; omit it and only workspace-wide routing rules apply. |
| question* | string | The complete question, up to 8000 characters. Preserve every point. Notifications show a short preview; the answer interface shows the full question. |
| toolName | string | The tool this approval is for (e.g. "Bash"), so the user can choose to always-allow it. |
| toolPath | string | MACHINE-POPULATED. Exact absolute Write file_path from the runtime, for diagnostic correlation only. Models must omit it. |
| agentName | string | Name of the agent asking, format "{Agent} - {project}" (e.g. "Claude Code - myproject"). Shown in the notification title so the user knows which session needs them. Falls back to the MCP client name if omitted. Cut to 100 chars. |
| machineId | string | Stable machine id of the asking agent, so two machines never collapse into one session. |
| questions | array | ONE question, in the richer Claude-compatible shape: a header, per-option descriptions, multiSelect, and an optional write-in. Exactly one keeps already-installed clients answerable; asking several means several calls. Runtime-populated; ordinary callers should omit it and use question/type/options. |
| requestId | string | MACHINE-POPULATED. The CALLER's own identifier for one logical invocation, used only when the runtime supplies no toolUseId. Mint it once, outside your retry loop, and send the same value on every attempt, so three retries of one ask become one decision. Do NOT derive it from the question text or reuse it across two deliberate asks: both collapse a real second question into the first one's answer. If you are a model deciding to call this tool, omit this field. |
| scopePath | string | Set ONLY when this approval exists because the path falls outside the scope the user ratified via propose_scope. Approving then widens the run scope to include this exact path, so the user is not asked again for the same area. |
| sessionId | string | Opaque per-session id of the asking agent, so parallel sessions are attributed separately. |
| timeoutMs | integer | How long this call blocks, in milliseconds (max 55000). Defaults to the site policy timeout. Ordinary questions stay open until answered or cancelled. A timeout ends this poll only; follow up with wait_for_answer. |
| toolUseId | string | MACHINE-POPULATED. The agent RUNTIME's own identifier for the tool call this approval gates, forwarded verbatim by a hook that received it. Do NOT invent, guess, derive, or reuse a value: two different questions sent under the same id collapse into one, and the second one never reaches a human. If you are a model deciding to call this tool, omit this field. |
| actionBody | string | The diff (Edit/Write) or full command (Bash/apply_patch), secret-redacted and size-capped. Rendered as a collapsible detail block; never used as the push body. |
| toolTarget | string | Compact target of the tool call (e.g. the command head "git push" for Bash, or a file extension like ".ts" for Edit/Write). Used to mine policy suggestions. |
| waitEndsAt | string | MACHINE-POPULATED. When the agent hook stops waiting live and hands control back to the terminal. The question may remain answerable after this time. Ordinary callers should omit it. |
| callbackUrl | string | Webhook URL that receives a POST with the answer when the user responds, signed with the X-Pushary-Signature header. Useful when the agent process may exit before the answer arrives. |
| externalIds | array | Deliver only to subscribers matching these external IDs. |
| pausedAware | boolean | MACHINE-POPULATED. true from a hook that ends a paused approval exactly as the same approval unanswered after its full wait. Any other value is ignored. If you are a model deciding to call this tool, omit this field. |
| placeholder | string | Hint text shown inside the free-text field for input questions |
| questionOwner | object | MACHINE-POPULATED. Ownership supplied by the supported personal Codex bridge. Models must omit this field. |
| subscriberIds | array | Deliver only to these subscriber IDs. Omit all targeting fields to reach every connected device. |
| permissionMode | string | MACHINE-POPULATED. The agent runtime's own permission or approval mode for the tool call this approval gates, recorded with the decision. Any other value is ignored. If you are a model deciding to call this tool, omit this field. |
| sessionToolAware | boolean | MACHINE-POPULATED. Set by a Pushary hook whose engine applies a session tool grant with its risky-command ceiling, so the grant is only offered where it takes effect. If you are a model deciding to call this tool, omit this field. |
| localSurfaceAvailable | boolean | MACHINE-POPULATED. false from a hook that cannot show its own approval prompt for this call, so the phone is asked every time and never spared for presence at the keyboard. Any other value is ignored. If you are a model deciding to call this tool, omit this field. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"question"
],
"properties": {
"env": {
"enum": [
"test"
],
"type": "string",
"description": "Set to \"test\" from a test suite. The question is stored and returned as pending, and nothing is delivered to a phone, browser or Slack. The X-Pushary-Env: test header does the same for every call on the connection."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Deliver only to subscribers that have any of these tags."
},
"type": {
"enum": [
"confirm",
"select",
"input"
],
"type": "string",
"default": "confirm",
"description": "Question type: confirm renders yes/no buttons, select renders the options list, input renders a free-text field."
},
"wait": {
"type": "boolean",
"default": true,
"description": "true (default) blocks until the user answers or the timeout fires. Set false to return immediately with a pending correlationId and poll it yourself via wait_for_answer."
},
"action": {
"type": "string",
"minLength": 0,
"description": "The concrete operation about to happen, one line. Shown as the Action line. Cut to 500 chars."
},
"intent": {
"type": "string",
"minLength": 0,
"description": "The user's stated task (from their last prompt), one line. Shown as the Intent line so the user can see why the agent stopped. Cut to 500 chars."
},
"blocker": {
"type": "string",
"minLength": 0,
"description": "The single gating reason the agent stopped, one line. Shown as the Blocker line. Cut to 500 chars."
},
"context": {
"type": "string",
"maxLength": 500,
"description": "One or two sentences about what the agent is working on, shown above the question so the user can decide without opening the terminal."
},
"options": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 6,
"minItems": 2,
"description": "The 2 to 6 choices for a select question. Required when type is \"select\", ignored otherwise. The answered value is the chosen option string."
},
"repoKey": {
"type": "string",
"maxLength": 200,
"description": "Stable repository identity for the working directory, e.g. \"github.com/acme/api\". Lets an approval routing rule scoped to one repository avoid governing another. Optional; omit it and only workspace-wide routing rules apply."
},
"question": {
"type": "string",
"maxLength": 8000,
"minLength": 1,
"description": "The complete question, up to 8000 characters. Preserve every point. Notifications show a short preview; the answer interface shows the full question."
},
"toolName": {
"type": "string",
"maxLength": 100,
"description": "The tool this approval is for (e.g. \"Bash\"), so the user can choose to always-allow it."
},
"toolPath": {
"type": "string",
"format": "starts_with",
"pattern": "^\\/.*",
"maxLength": 4096,
"description": "MACHINE-POPULATED. Exact absolute Write file_path from the runtime, for diagnostic correlation only. Models must omit it."
},
"agentName": {
"type": "string",
"minLength": 0,
"description": "Name of the agent asking, format \"{Agent} - {project}\" (e.g. \"Claude Code - myproject\"). Shown in the notification title so the user knows which session needs them. Falls back to the MCP client name if omitted. Cut to 100 chars."
},
"machineId": {
"type": "string",
"maxLength": 128,
"description": "Stable machine id of the asking agent, so two machines never collapse into one session."
},
"questions": {
"type": "array",
"items": {
"type": "object",
"required": [
"question",
"multiSelect",
"options"
],
"properties": {
"header": {
"type": "string",
"maxLength": 40
},
"options": {
"type": "array",
"items": {
"type": "object",
"required": [
"label"
],
"properties": {
"label": {
"type": "string",
"maxLength": 100,
"minLength": 1
},
"description": {
"type": "string",
"maxLength": 500
}
}
},
"maxItems": 4,
"minItems": 2
},
"question": {
"type": "string",
"maxLength": 8000,
"minLength": 1
},
"allowOther": {
"type": "boolean"
},
"multiSelect": {
"type": "boolean"
}
}
},
"maxItems": 1,
"minItems": 1,
"description": "ONE question, in the richer Claude-compatible shape: a header, per-option descriptions, multiSelect, and an optional write-in. Exactly one keeps already-installed clients answerable; asking several means several calls. Runtime-populated; ordinary callers should omit it and use question/type/options."
},
"requestId": {
"type": "string",
"maxLength": 200,
"description": "MACHINE-POPULATED. The CALLER's own identifier for one logical invocation, used only when the runtime supplies no toolUseId. Mint it once, outside your retry loop, and send the same value on every attempt, so three retries of one ask become one decision. Do NOT derive it from the question text or reuse it across two deliberate asks: both collapse a real second question into the first one's answer. If you are a model deciding to call this tool, omit this field."
},
"scopePath": {
"type": "string",
"maxLength": 200,
"description": "Set ONLY when this approval exists because the path falls outside the scope the user ratified via propose_scope. Approving then widens the run scope to include this exact path, so the user is not asked again for the same area."
},
"sessionId": {
"type": "string",
"maxLength": 128,
"description": "Opaque per-session id of the asking agent, so parallel sessions are attributed separately."
},
"timeoutMs": {
"type": "integer",
"maximum": 55000,
"minimum": 1000,
"description": "How long this call blocks, in milliseconds (max 55000). Defaults to the site policy timeout. Ordinary questions stay open until answered or cancelled. A timeout ends this poll only; follow up with wait_for_answer."
},
"toolUseId": {
"type": "string",
"maxLength": 200,
"description": "MACHINE-POPULATED. The agent RUNTIME's own identifier for the tool call this approval gates, forwarded verbatim by a hook that received it. Do NOT invent, guess, derive, or reuse a value: two different questions sent under the same id collapse into one, and the second one never reaches a human. If you are a model deciding to call this tool, omit this field."
},
"actionBody": {
"type": "string",
"maxLength": 4000,
"description": "The diff (Edit/Write) or full command (Bash/apply_patch), secret-redacted and size-capped. Rendered as a collapsible detail block; never used as the push body."
},
"toolTarget": {
"type": "string",
"maxLength": 80,
"description": "Compact target of the tool call (e.g. the command head \"git push\" for Bash, or a file extension like \".ts\" for Edit/Write). Used to mine policy suggestions."
},
"waitEndsAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
"description": "MACHINE-POPULATED. When the agent hook stops waiting live and hands control back to the terminal. The question may remain answerable after this time. Ordinary callers should omit it."
},
"callbackUrl": {
"type": "string",
"format": "uri",
"description": "Webhook URL that receives a POST with the answer when the user responds, signed with the X-Pushary-Signature header. Useful when the agent process may exit before the answer arrives."
},
"externalIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Deliver only to subscribers matching these external IDs."
},
"pausedAware": {
"type": "boolean",
"description": "MACHINE-POPULATED. true from a hook that ends a paused approval exactly as the same approval unanswered after its full wait. Any other value is ignored. If you are a model deciding to call this tool, omit this field."
},
"placeholder": {
"type": "string",
"maxLength": 200,
"description": "Hint text shown inside the free-text field for input questions"
},
"questionOwner": {
"type": "object",
"properties": {},
"description": "MACHINE-POPULATED. Ownership supplied by the supported personal Codex bridge. Models must omit this field.",
"additionalProperties": {}
},
"subscriberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Deliver only to these subscriber IDs. Omit all targeting fields to reach every connected device."
},
"permissionMode": {
"type": "string",
"maxLength": 64,
"description": "MACHINE-POPULATED. The agent runtime's own permission or approval mode for the tool call this approval gates, recorded with the decision. Any other value is ignored. If you are a model deciding to call this tool, omit this field."
},
"sessionToolAware": {
"type": "boolean",
"description": "MACHINE-POPULATED. Set by a Pushary hook whose engine applies a session tool grant with its risky-command ceiling, so the grant is only offered where it takes effect. If you are a model deciding to call this tool, omit this field."
},
"localSurfaceAvailable": {
"type": "boolean",
"description": "MACHINE-POPULATED. false from a hook that cannot show its own approval prompt for this call, so the phone is asked every time and never spared for presence at the keyboard. Any other value is ignored. If you are a model deciding to call this tool, omit this field."
}
}
}Retract a pending question that is no longer needed or is moving to the current client. Returns cancelled:true only when a pending question was removed. False may mean it was already answered, expired, missing or unavailable. An unavailable state includes handoffAction:stop because cancellation could not safely inspect or fence the question. Does not retract an answer already recorded.
| Argument | Type | Description |
|---|---|---|
| handoff | boolean | True when you are cancelling because you are about to ask the same question in the current client. For the next minute the Pushary hook then lets your own question tool through instead of sending it back to the phone. Set automatically whenever a live question is cancelled. |
| correlationId* | string | The correlationId of the pending question to cancel, as returned by ask_user or send_notification |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"correlationId"
],
"properties": {
"handoff": {
"type": "boolean",
"description": "True when you are cancelling because you are about to ask the same question in the current client. For the next minute the Pushary hook then lets your own question tool through instead of sending it back to the phone. Set automatically whenever a live question is cancelled."
},
"correlationId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The correlationId of the pending question to cancel, as returned by ask_user or send_notification"
}
}
}Request agreement on an unresolved or user-requested boundary for this session. Sends a real scope question and returns ratified, answered, contract and enforcement information. Only supported hooks can enforce file paths on path-bearing tool calls; promises are recorded but not enforced. Empty enforces means no automatic boundary checking. A contract never overrides host permissions or authorizes a separate action. The call waits at most 55 seconds; a pending proposal remains answerable after the call returns.
| Argument | Type | Description |
|---|---|---|
| doneWhen* | string | What "finished" means for this run, one or two lines. Carried for the human to judge against; never enforced automatically. |
| promises | array | Boundaries that are not file paths: recipients, channels, spend limits, systems you will not open. For an agent whose work is not code (marketing, sales, support, operations), this is where the boundary goes. Shown to the user labelled "Promised, not checked" and recorded in the ledger, but NEVER enforced, because the gate judges a file path and these have none. Do not put these in allowedPaths. |
| agentName | string | Name of the agent asking, format "{Agent} - {project}". |
| machineId | string | Stable machine id, so two machines never collapse into one session. |
| sessionId* | string | Your per-session id, as your client reports it for THIS run. Required, and it is the key the gate reads the contract back by: a value that matches no live session still returns ratified:true and enforces nothing. Never invent one, and never reuse one from another run. |
Schedule, list, retrieve or cancel a personal Pushary reminder for the authenticated user. Scheduled reminders can arrive after the agent exits. One-time only; up to 30 days away, 25 pending per user/site. Scheduling requires body and either inMinutes or at with an explicit UTC offset. requestId identifies one schedule across retries. reminderId retrieves its outcome; no arguments lists pending reminders; cancelReminderId cancels a pending reminder. Returns the scheduled time, state and any delivery warning. Delivery respects stop/mute and active membership. Does not schedule agent work or customer campaigns. API reference: https://pushary.com/docs/agents/reference/tools#schedule_reminder
| Argument | Type | Description |
|---|---|---|
| at | string | Future ISO 8601 timestamp with explicit UTC offset or Z, at most 30 days away. Resolve the user's local time and timezone before scheduling; never guess a timezone. |
| env | string | |
| body | string | What to remind the authenticated user about, at most 180 UTF-16 units after secret redaction (emoji may use two). Omit all fields to list their pending reminders. |
| title | string | |
| agentName | string | |
| inMinutes | integer | Minutes from the server clock; use for timers and relative reminders. |
Send a notification to connected devices for a requested update or a meaningful unattended result, such as a finished task. Delivery follows the account policy and optional recipient filters. context adds a detail page; context.askQuestion creates a linked decision. Returns web/mobile delivery counts and a warning when no delivery channel is connected. Sends real notifications. API reference: https://pushary.com/docs/agents/reference/tools#send_notification
| Argument | Type | Description |
|---|---|---|
| env | string | Set to "test" from a test suite. The notification is recorded in the activity feed and nothing is delivered to a phone or browser. The X-Pushary-Env: test header does the same for every call on the connection. |
| url | string | URL opened when the user taps the notification. Ignored if context is provided, because a context detail page URL is generated automatically. |
| body* | string | Notification body text (max 500 chars). One or two sentences the user can act on without opening anything. |
| tags | array | Deliver only to subscribers that have any of these tags. |
| title* | string | Notification title shown on the lock screen (max 100 chars). Lead with the outcome, e.g. "Build finished" or "Migration failed". |
| context | object |
Read the answer or current state of an existing Pushary question by its correlationId. Waits up to timeoutMs, capped at 55 seconds. Returns answered, the answer when available, status and handoff information. Only pending is a live unanswered question; cancelled, expired, missing and unavailable are terminal states. Does not send another question.
| Argument | Type | Description |
|---|---|---|
| timeoutMs | integer | How long this one poll blocks, in milliseconds (default 30000, max 55000). Follow handoffAction when present, otherwise nextAction. |
| correlationId* | string | The correlationId from an earlier ask_user response, or the linkedCorrelationId from a send_notification with an embedded askQuestion |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"correlationId"
],
"properties": {
"timeoutMs": {
"type": "integer",
"maximum": 55000,
"minimum": 1000,
"description": "How long this one poll blocks, in milliseconds (default 30000, max 55000). Follow handoffAction when present, otherwise nextAction."
},
"correlationId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The correlationId from an earlier ask_user response, or the linkedCorrelationId from a send_notification with an embedded askQuestion"
}
}
}| timeoutMs | integer | How long this call blocks, in milliseconds (max 55000). |
| allowedPaths | array | Globs you intend to change, e.g. ["src/**", "docs/*.md"]. File paths only: a word that is not a path ("hubspot", "summer-campaign") matches no file and makes every edit read as out of scope. A bare directory is expanded for you, so "docs" also covers "docs/**". Omit or leave empty to propose no path restriction, which the user is told plainly. |
| offLimitsPaths | array | Globs you promise not to touch, e.g. [".env*", "infra/**"]. These win wherever they overlap allowedPaths. A leading "**/" needs a directory before it, so "**/.env*" is expanded for you to also cover a root ".env". |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"doneWhen",
"sessionId"
],
"properties": {
"doneWhen": {
"type": "string",
"maxLength": 300,
"minLength": 1,
"description": "What \"finished\" means for this run, one or two lines. Carried for the human to judge against; never enforced automatically."
},
"promises": {
"type": "array",
"items": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"maxItems": 10,
"description": "Boundaries that are not file paths: recipients, channels, spend limits, systems you will not open. For an agent whose work is not code (marketing, sales, support, operations), this is where the boundary goes. Shown to the user labelled \"Promised, not checked\" and recorded in the ledger, but NEVER enforced, because the gate judges a file path and these have none. Do not put these in allowedPaths."
},
"agentName": {
"type": "string",
"maxLength": 100,
"description": "Name of the agent asking, format \"{Agent} - {project}\"."
},
"machineId": {
"type": "string",
"maxLength": 128,
"description": "Stable machine id, so two machines never collapse into one session."
},
"sessionId": {
"type": "string",
"maxLength": 128,
"minLength": 1,
"description": "Your per-session id, as your client reports it for THIS run. Required, and it is the key the gate reads the contract back by: a value that matches no live session still returns ratified:true and enforces nothing. Never invent one, and never reuse one from another run."
},
"timeoutMs": {
"type": "integer",
"maximum": 55000,
"minimum": 1000,
"description": "How long this call blocks, in milliseconds (max 55000)."
},
"allowedPaths": {
"type": "array",
"items": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"maxItems": 40,
"description": "Globs you intend to change, e.g. [\"src/**\", \"docs/*.md\"]. File paths only: a word that is not a path (\"hubspot\", \"summer-campaign\") matches no file and makes every edit read as out of scope. A bare directory is expanded for you, so \"docs\" also covers \"docs/**\". Omit or leave empty to propose no path restriction, which the user is told plainly."
},
"offLimitsPaths": {
"type": "array",
"items": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"maxItems": 40,
"description": "Globs you promise not to touch, e.g. [\".env*\", \"infra/**\"]. These win wherever they overlap allowedPaths. A leading \"**/\" needs a directory before it, so \"**/.env*\" is expanded for you to also cover a root \".env\"."
}
}
}| machineId |
| string |
| requestId | string | Unique ID for this scheduling operation. Reuse it unchanged on retries to return the original reminder, including after it settles. |
| sessionId | string |
| reminderId | string | Look up your reminder, including its final outcome and delivery status, instead of scheduling or cancelling. |
| cancelReminderId | string | Cancel this user's scheduled reminder instead of creating one. Already-dispatching reminders cannot be cancelled. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
"description": "Future ISO 8601 timestamp with explicit UTC offset or Z, at most 30 days away. Resolve the user's local time and timezone before scheduling; never guess a timezone."
},
"env": {
"type": "string",
"const": "test"
},
"body": {
"type": "string",
"maxLength": 180,
"minLength": 1,
"description": "What to remind the authenticated user about, at most 180 UTF-16 units after secret redaction (emoji may use two). Omit all fields to list their pending reminders."
},
"title": {
"type": "string",
"maxLength": 100,
"minLength": 1
},
"agentName": {
"type": "string",
"maxLength": 100
},
"inMinutes": {
"type": "integer",
"maximum": 43200,
"minimum": 1,
"description": "Minutes from the server clock; use for timers and relative reminders."
},
"machineId": {
"type": "string",
"maxLength": 128
},
"requestId": {
"type": "string",
"maxLength": 128,
"minLength": 1,
"description": "Unique ID for this scheduling operation. Reuse it unchanged on retries to return the original reminder, including after it settles."
},
"sessionId": {
"type": "string",
"maxLength": 128
},
"reminderId": {
"type": "string",
"maxLength": 128,
"minLength": 1,
"description": "Look up your reminder, including its final outcome and delivery status, instead of scheduling or cancelling."
},
"cancelReminderId": {
"type": "string",
"maxLength": 128,
"minLength": 1,
"description": "Cancel this user's scheduled reminder instead of creating one. Already-dispatching reminders cannot be cancelled."
}
}
}| Structured context rendered as a rich detail page when the user taps the notification. Strongly recommended for task_complete and error notifications so the user can act from their phone. |
| iconUrl | string | URL of the notification icon image |
| imageUrl | string | URL of a large image shown in the notification |
| agentName | string | Name of the agent sending this notification, format "{Agent} - {project}" (e.g. "Claude Code - myproject"). Shown in the notification so the user knows which session is talking. Falls back to the MCP client name if omitted. |
| machineId | string | Stable machine id of the sending agent, so two machines never collapse into one session. |
| sessionId | string | Opaque per-session id of the sending agent, so parallel sessions are attributed separately in the activity feed. |
| externalIds | array | Deliver only to subscribers matching these external IDs. |
| subscriberIds | array | Deliver only to these subscriber IDs. Omit all targeting fields to reach every connected device. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"title",
"body"
],
"properties": {
"env": {
"enum": [
"test"
],
"type": "string",
"description": "Set to \"test\" from a test suite. The notification is recorded in the activity feed and nothing is delivered to a phone or browser. The X-Pushary-Env: test header does the same for every call on the connection."
},
"url": {
"type": "string",
"format": "uri",
"description": "URL opened when the user taps the notification. Ignored if context is provided, because a context detail page URL is generated automatically."
},
"body": {
"type": "string",
"maxLength": 500,
"minLength": 1,
"description": "Notification body text (max 500 chars). One or two sentences the user can act on without opening anything."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Deliver only to subscribers that have any of these tags."
},
"title": {
"type": "string",
"maxLength": 100,
"minLength": 1,
"description": "Notification title shown on the lock screen (max 100 chars). Lead with the outcome, e.g. \"Build finished\" or \"Migration failed\"."
},
"context": {
"type": "object",
"required": [
"type"
],
"properties": {
"type": {
"enum": [
"task_complete",
"error",
"info"
],
"type": "string",
"description": "What kind of update this is. Use \"task_complete\" when work finished, \"error\" when something failed (delivered with high urgency), \"info\" for everything else. Error severity describes this notification, not the agent session health."
},
"details": {
"type": "array",
"items": {
"type": "string"
},
"description": "Bullet-point details rendered as a list"
},
"summary": {
"type": "string",
"description": "Short summary of what happened, shown at the top of the detail page"
},
"errorFile": {
"type": "string",
"description": "File path where the error occurred"
},
"nextSteps": {
"type": "string",
"description": "What the user should do next, e.g. \"Review the PR\" or \"Re-run with --force\""
},
"askQuestion": {
"type": "object",
"required": [
"question"
],
"properties": {
"type": {
"enum": [
"confirm",
"select",
"input"
],
"type": "string",
"default": "confirm",
"description": "Question type: confirm (yes/no), select (pick from options), or input (free text)"
},
"options": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 6,
"minItems": 2,
"description": "The 2 to 6 choices for a select question"
},
"question": {
"type": "string",
"maxLength": 8000,
"minLength": 1,
"description": "A follow-up question shown below the context (e.g. \"Retry with a different approach?\")"
}
},
"description": "Embed a decision prompt on the detail page. The response includes a linkedCorrelationId; pass it to wait_for_answer to collect the answer. The embedded question expires 10 minutes after it is created. Only for a decision about unfinished work. Not for asking what to do next once the requested work is complete, unless the user asked you to."
},
"errorMessage": {
"type": "string",
"description": "The error message, when type is \"error\""
},
"filesChanged": {
"type": "array",
"items": {
"type": "string"
},
"description": "Paths of files that were created or modified"
}
},
"description": "Structured context rendered as a rich detail page when the user taps the notification. Strongly recommended for task_complete and error notifications so the user can act from their phone."
},
"iconUrl": {
"type": "string",
"format": "uri",
"description": "URL of the notification icon image"
},
"imageUrl": {
"type": "string",
"format": "uri",
"description": "URL of a large image shown in the notification"
},
"agentName": {
"type": "string",
"maxLength": 100,
"description": "Name of the agent sending this notification, format \"{Agent} - {project}\" (e.g. \"Claude Code - myproject\"). Shown in the notification so the user knows which session is talking. Falls back to the MCP client name if omitted."
},
"machineId": {
"type": "string",
"maxLength": 128,
"description": "Stable machine id of the sending agent, so two machines never collapse into one session."
},
"sessionId": {
"type": "string",
"maxLength": 128,
"description": "Opaque per-session id of the sending agent, so parallel sessions are attributed separately in the activity feed."
},
"externalIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Deliver only to subscribers matching these external IDs."
},
"subscriberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Deliver only to these subscriber IDs. Omit all targeting fields to reach every connected device."
}
}
}