Noddocs

Requests

Ask users for approvals, choices, answers, and permissions

Request types

KindUser actionExample
approvalTap Approve or Reject"Deploy to production?"
choiceTap one of the options, or type a custom answer via the built-in "Other" field"PostgreSQL / SQLite / MongoDB"
questionType a free-text answer"What should I name the project?"
permissionAllow, Always Allow, or Deny"Allow: Bash(npm install stripe)"
POST/api/agent/events

Create a request via REST. The request appears as an interactive card in the user's Nod app.

typerequired
string
Must be "decision"
titlerequired
string
Card title shown to the user
descriptionrequired
string
Detail text under the title
kindrequired
string
"approval" | "question" | "choice" | "permission"
options
string[]
Options for choice requests
conversation_id
string
Conversation to show the card in. Null = requests inbox only.
task_run_id
string
Associate this request with a task run. Returned in the resolution payload.
Approval
POST /api/agent/events
X-Nod-Agent-Id: agt_abc123
X-Nod-Secret: nod_abc123...

{
  "type": "decision",
  "title": "Deploy to production?",
  "description": "Build #42 passed all tests.",
  "kind": "approval"
}
Choice
{
  "type": "decision",
  "title": "Database?",
  "description": "Which database for this project?",
  "kind": "choice",
  "options": ["PostgreSQL", "SQLite", "MongoDB"]
  // No need to add "Other" — the app automatically includes
  // a free-text field so the user can type a custom answer.
}
Question
{
  "type": "decision",
  "title": "Project name?",
  "description": "What should I name the project?",
  "kind": "question"
}
Response
{
  "status": "ok",
  "decision_id": "dec_xyz"
}
POSTWebSocket frame

Create a request via WebSocket. Same fields, plus permission-specific options.

allows_always
boolean
Show "Always Allow" option (permission kind)
always_allow_label
string
Label for the always-allow button
always_allow_options
array
Patterns the user can choose to always allow
Approval request
{
  "type": "decision",
  "title": "Deploy to production?",
  "description": "Build #42 passed all tests.",
  "kind": "approval",
  "conversation_id": "conv_abc123"
}
Permission request
{
  "type": "decision",
  "title": "Allow: Bash(npm install stripe)",
  "description": "The agent wants to run this command.",
  "kind": "permission",
  "allows_always": true,
  "always_allow_options": [
    { "pattern": "Bash(npm install *)", "label": "All npm installs" },
    { "pattern": "Bash(npm install stripe)", "label": "This command" }
  ],
  "conversation_id": "conv_abc123"
}

Receiving responses

When the user acts on a request, your agent receives a single decision_resolved event via WebSocket or webhook. Check the status field to determine what happened:

StatusMeaningRead from
approvedUser approved the requestnote (optional comment)
rejectedUser rejected the requestnote (optional reason)
respondedUser answered a question or choice requestnote (the answer text)
dismissedUser dismissed without action
GETdecision_resolved event

Delivered via WebSocket if online, otherwise POSTed to your agent's webhook_url.

decision_id
string
The request that was resolved
status
string
"approved" | "rejected" | "responded" (question/choice answered) | "dismissed"
note
string?
User's comment, typed answer, or selected option
title
string
Original request title (echoed back for context)
description
string
Original request description (echoed back for context)
created_at
string
ISO 8601 timestamp of when the request was originally created.
task_run_id
string?
If this decision was created during a task run, the run ID. Use to fetch task context.
always_allow
boolean?
Whether user chose "Always Allow"
always_allow_pattern
string?
The pattern they chose to always allow

Dismissed requests do not notify the agent

When a user dismisses a request, the decision is marked as resolved in the database but no decision_resolved event is delivered to the agent. Your agent will not receive any notification — design your timeout handling accordingly.
Approved
{
  "type": "decision_resolved",
  "decision_id": "dec_xyz",
  "status": "approved",
  "note": "Go ahead",
  "title": "Deploy to production?",
  "description": "Build #42 passed all tests.",
  "conversation_id": "conv_abc123"
}
Question answered
{
  "type": "decision_resolved",
  "decision_id": "dec_xyz",
  "status": "responded",
  "note": "nod-payments-api",
  "title": "Project name?",
  "description": "What should I name the new project?",
  "conversation_id": "conv_abc123"
}
Permission always-allow
{
  "type": "decision_resolved",
  "decision_id": "dec_xyz",
  "status": "approved",
  "always_allow": true,
  "always_allow_pattern": "Bash(npm install *)"
}

Resolve your own requests

If the user answers in chat instead of tapping the card, resolve the pending request yourself so it doesn't stay open.

POST/api/agent/decisions/resolve

Resolve a non-permission request created by your agent.

decision_idrequired
string
The request to resolve
note
string
Resolution note
Request
POST /api/agent/decisions/resolve
X-Nod-Agent-Id: agt_abc123
X-Nod-Secret: nod_abc123...

{
  "decision_id": "dec_xyz",
  "note": "User said in chat: use PostgreSQL"
}
Response
{
  "status": "resolved",
  "decision_id": "dec_xyz"
}

Task proposals

Propose a scheduled task to the user. If approved, the task is auto-created.

POST/api/agent/events

Send a task proposal.

typerequired
string
Must be "task_proposal"
titlerequired
string
Card title
task_namerequired
string
Task name
task_promptrequired
string
Task instructions
task_schedulerequired
string
Cron expression or "once"
Request
{
  "type": "task_proposal",
  "title": "Schedule: Daily summary",
  "task_name": "Daily summary",
  "task_prompt": "Summarize git activity",
  "task_schedule": "0 9 * * 1-5"
}