Requests & Decisions
Structured ways for agents to get human input: approvals, choices, questions, and permissions
Overview
Requests are structured interactions where the agent asks for human input and waits for a response. Unlike chat messages, requests have defined response types — users respond by tapping a button, selecting an option, or typing an answer.
In the Nod app, requests appear as interactive cards. Users can respond from the chat view or the dedicated Requests page.
Four request types
| Kind | User action | Response | Example |
|---|---|---|---|
| approval | Tap Approve or Reject | status: approved/rejected + optional note | "Deploy to production?" |
| choice | Tap an option or type a custom answer via "Other" | status: responded + selected option in note | "PostgreSQL / SQLite / MongoDB" |
| question | Type a free-text answer | status: responded + answer in note | "What should I name the project?" |
| permission | Allow, Always Allow, or Deny | status: approved/rejected + optional pattern | "Allow: Bash(npm install stripe)" |
Approval requests
The simplest request type. The agent asks a yes/no question. The user taps Approve or Reject.
{
"type": "decision",
"title": "Deploy to production?",
"description": "Build #42 passed all tests. Ready to deploy to the production cluster.",
"kind": "approval"
}{
"type": "decision_resolved",
"decision_id": "dec_xyz",
"status": "approved", // or "rejected"
"note": "Go ahead", // optional note from user
"conversation_id": "conv_abc123"
}Choice requests
Present a set of options for the user to pick from. Options render as tappable buttons in the Nod app.
{
"type": "decision",
"title": "Database?",
"description": "Which database should we use for this project?",
"kind": "choice",
"options": ["PostgreSQL", "SQLite", "MongoDB"]
}The Nod app renders each option as a tappable button. An "Other..." button is automatically included below the options — tapping it opens a free-text field where the user can type a custom response instead of picking a predefined option. You don't need to add "Other" to your options array; the app handles it for you. Either way, the user's selection (or typed text) appears in the note field of the resolution.
Question requests
Ask for free-text input. The user sees a text field and can type any response.
{
"type": "decision",
"title": "Project name?",
"description": "What should I name the new project?",
"kind": "question"
}When the user submits their answer, your agent receives a decision_resolved event with status: "responded" and the typed text in the note field.
Permission requests
Permission requests are typically used by hook systems (like our CLI's PreToolUse hook) to gate specific agent actions. They include "Always Allow" options so users can persist permission patterns.
{
"type": "decision",
"title": "Allow: Bash(npm install stripe)",
"description": "The agent wants to run: npm install stripe",
"kind": "permission",
"allows_always": true,
"always_allow_label": "Always allow npm install",
"always_allow_options": [
{ "pattern": "Bash(npm install *)", "label": "All npm installs" },
{ "pattern": "Bash(npm install stripe)", "label": "This exact command" }
]
}When the user taps "Always Allow" and selects a pattern, it's returned in the resolution so your system can persist the rule.
Request lifecycle
| Status | Meaning |
|---|---|
| pending | Waiting for user response |
| approved | User approved (approval/permission kind) |
| rejected | User rejected (approval/permission kind) |
| responded | User responded with text or selection (question/choice kind) |
| dismissed | User dismissed without responding |
Your agent receives the resolution via WebSocket (decision_resolved event) or webhook delivery. The resolution includes the status, optional note, and any "always allow" pattern the user selected.
Requests without a conversation
Requests sent during task runs or from approval-only agents may not have a conversation context. Set conversation_id to null to create a request that appears only in the Requests page, not in any chat thread:
{
"type": "decision",
"title": "Approve refund #4821?",
"description": "$730 refund to customer@example.com",
"kind": "approval",
"conversation_id": null
}