> For AI agents: the documentation index is at https://docs.gethandup.dev/llms.txt. Append .md to any page URL (without the trailing slash) for Markdown, for example https://docs.gethandup.dev/cli.md.

# Event schema

Generated by `handup schema event` (`make docs`) into `docs/public/schema/event.schema.json`. Raw file: [/schema/event.schema.json](https://docs.gethandup.dev/schema/event.schema.json).

## Fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `v` | `integer` | yes | Envelope version, currently 1. |
| `type` | `EventType` | yes |  |
| `id` | `string` | yes | Event identifier (ULID), distinct from the request identifier. |
| `time` | `string` | yes | Event time in RFC3339 UTC. |
| `data` | `EventData` | yes |  |

## Definitions

### EventType

`"request.created" | "request.decided" | "request.expired" | "request.cancelled" | "request.reminder" | "request.expiring"`

### EventData

| Field | Type | Required |
| --- | --- | --- |
| `request` | `EventRequest` | yes |
| `decision` | `Decision \| null` |  |

### EventRequest

| Field | Type | Required |
| --- | --- | --- |
| `id` | `string` | yes |
| `title` | `string` | yes |
| `risk` | `Risk` | yes |
| `agent` | `string \| null` |  |
| `source` | `Source` | yes |
| `status` | `Status` | yes |
| `url` | `string` | yes |
| `deep_link` | `string` | yes |
| `content` | `EventContent \| null` |  |

### Risk

`"low" | "medium" | "high"`

### Source

| Field | Type | Required |
| --- | --- | --- |
| `integration` | `string \| null` |  |
| `agent` | `string \| null` |  |
| `session` | `string \| null` |  |
| `session_title` | `string \| null` |  |
| `cwd` | `string \| null` |  |
| `repo` | `string \| null` |  |
| `branch` | `string \| null` |  |
| `host` | `string \| null` |  |

### Status

`"pending" | "approved" | "denied" | "answered" | "dismissed" | "expired" | "cancelled"`

### EventContent

| Field | Type | Required |
| --- | --- | --- |
| `summary` | `string \| null` |  |
| `previews` | `Preview[]` | yes |

### Preview

| Field | Type | Required |
| --- | --- | --- |
| `type` | `PreviewType` | yes |
| `title` | `string \| null` |  |
| `lang` | `string \| null` |  |
| `inline` | `string \| null` |  |
| `blob` | `string \| null` |  |
| `mime` | `string \| null` |  |
| `name` | `string \| null` |  |
| `size` | `integer \| null` |  |
| `files` | `PreviewFile[] \| null` |  |
| `entry` | `string \| null` |  |
| `command` | `Command \| null` |  |
| `email` | `Email \| null` |  |
| `before_blob` | `string \| null` |  |
| `after_blob` | `string \| null` |  |

### PreviewType

`"text" | "markdown" | "code" | "diff" | "files" | "command" | "json" | "html" | "image" | "video" | "audio" | "file" | "pdf" | "email"`

### PreviewFile

| Field | Type | Required |
| --- | --- | --- |
| `path` | `string` | yes |
| `blob` | `string` | yes |
| `mime` | `string` | yes |
| `size` | `integer` | yes |
| `lang` | `string \| null` |  |

### Command

| Field | Type | Required |
| --- | --- | --- |
| `shell` | `string \| null` |  |
| `argv` | `string[] \| null` |  |
| `cwd` | `string \| null` |  |
| `env_keys` | `string[]` |  |

### Email

An email draft for review. handup never sends it: on approval the agent
sends the draft (or the edited `decision.fields`) with its own mail tool.

| Field | Type | Required |
| --- | --- | --- |
| `from` | `string \| null` |  |
| `to` | `string[]` |  |
| `cc` | `string[]` |  |
| `bcc` | `string[]` |  |
| `reply_to` | `string \| null` |  |
| `subject` | `string` |  |
| `date` | `string \| null` |  |
| `body` | `string` |  |
| `body_html` | `string \| null` |  |
| `attachments` | `EmailAttachment[]` |  |
| `in_reply_to` | `EmailQuote \| null` |  |

### EmailAttachment

| Field | Type | Required |
| --- | --- | --- |
| `name` | `string` | yes |
| `mime` | `string \| null` |  |
| `size` | `integer \| null` |  |
| `blob` | `string \| null` |  |

### EmailQuote

| Field | Type | Required |
| --- | --- | --- |
| `from` | `string \| null` |  |
| `date` | `string \| null` |  |
| `body` | `string` |  |

### Decision

| Field | Type | Required |
| --- | --- | --- |
| `option` | `string` | yes |
| `feedback` | `string \| null` |  |
| `fields` | `DecisionFields \| null` |  |
| `content_hash` | `string` | yes |
| `decided_by` | `string \| null` |  |
| `decided_at` | `integer \| null` |  |
| `outcome` | `Outcome \| null` |  |
| `rule_id` | `string \| null` |  |
| `scope` | `string \| null` |  |
| `run_result` | `RunResult \| null` |  |

### DecisionFields

Decision `fields`. Question requests carry only `answers`, keyed by question
id; other requests carry answers to `input.fields` or edited tool input.

| Field | Type | Required |
| --- | --- | --- |
| `answers` | `object \| null` |  |

### Answer

The human's answer to one question.

| Field | Type | Required |
| --- | --- | --- |
| `selected` | `string[]` |  |
| `text` | `string \| null` |  |

### Outcome

`"approve" | "deny"`

### RunResult

Outcome of running a command request in the handup desktop app. The
command already ran: an agent must not run it again. Output is the
redacted tail of each stream.

| Field | Type | Required |
| --- | --- | --- |
| `exit_code` | `integer \| null` |  |
| `stdout_tail` | `string` |  |
| `stderr_tail` | `string` |  |
| `duration_ms` | `integer` |  |
| `truncated` | `boolean` |  |
| `elevated` | `boolean` |  |
| `error` | `string \| null` |  |

## JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "EventEnvelope",
  "type": "object",
  "properties": {
    "v": {
      "description": "Envelope version, currently 1.",
      "type": "integer",
      "format": "uint8",
      "minimum": 0,
      "maximum": 255
    },
    "type": {
      "$ref": "#/$defs/EventType"
    },
    "id": {
      "description": "Event identifier (ULID), distinct from the request identifier.",
      "type": "string"
    },
    "time": {
      "description": "Event time in RFC3339 UTC.",
      "type": "string"
    },
    "data": {
      "$ref": "#/$defs/EventData"
    }
  },
  "additionalProperties": false,
  "required": [
    "v",
    "type",
    "id",
    "time",
    "data"
  ],
  "$defs": {
    "EventType": {
      "type": "string",
      "enum": [
        "request.created",
        "request.decided",
        "request.expired",
        "request.cancelled",
        "request.reminder",
        "request.expiring"
      ]
    },
    "EventData": {
      "type": "object",
      "properties": {
        "request": {
          "$ref": "#/$defs/EventRequest"
        },
        "decision": {
          "anyOf": [
            {
              "$ref": "#/$defs/Decision"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "additionalProperties": false,
      "required": [
        "request"
      ]
    },
    "EventRequest": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "title": {
          "type": "string"
        },
        "risk": {
          "$ref": "#/$defs/Risk"
        },
        "agent": {
          "type": [
            "string",
            "null"
          ]
        },
        "source": {
          "$ref": "#/$defs/Source"
        },
        "status": {
          "$ref": "#/$defs/Status"
        },
        "url": {
          "type": "string"
        },
        "deep_link": {
          "type": "string"
        },
        "content": {
          "anyOf": [
            {
              "$ref": "#/$defs/EventContent"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "additionalProperties": false,
      "required": [
        "id",
        "title",
        "risk",
        "source",
        "status",
        "url",
        "deep_link"
      ]
    },
    "Risk": {
      "type": "string",
      "enum": [
        "low",
        "medium",
        "high"
      ]
    },
    "Source": {
      "type": "object",
      "properties": {
        "integration": {
          "description": "Server-verified integration token name; caller values are overwritten.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "agent": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "session": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "session_title": {
          "description": "Display-only session name; scoped rules always use the session ID.",
          "type": [
            "string",
            "null"
          ]
        },
        "cwd": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "repo": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "branch": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "host": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        }
      },
      "additionalProperties": false
    },
    "Status": {
      "type": "string",
      "enum": [
        "pending",
        "approved",
        "denied",
        "answered",
        "dismissed",
        "expired",
        "cancelled"
      ]
    },
    "EventContent": {
      "type": "object",
      "properties": {
        "summary": {
          "type": [
            "string",
            "null"
          ]
        },
        "previews": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Preview"
          }
        }
      },
      "additionalProperties": false,
      "required": [
        "previews"
      ]
    },
    "Preview": {
      "type": "object",
      "properties": {
        "type": {
          "$ref": "#/$defs/PreviewType"
        },
        "title": {
          "type": [
            "string",
            "null"
          ]
        },
        "lang": {
          "type": [
            "string",
            "null"
          ]
        },
        "inline": {
          "type": [
            "string",
            "null"
          ]
        },
        "blob": {
          "type": [
            "string",
            "null"
          ]
        },
        "mime": {
          "type": [
            "string",
            "null"
          ]
        },
        "name": {
          "type": [
            "string",
            "null"
          ]
        },
        "size": {
          "type": [
            "integer",
            "null"
          ],
          "format": "uint64",
          "minimum": 0
        },
        "files": {
          "type": [
            "array",
            "null"
          ],
          "items": {
            "$ref": "#/$defs/PreviewFile"
          }
        },
        "entry": {
          "type": [
            "string",
            "null"
          ]
        },
        "command": {
          "anyOf": [
            {
              "$ref": "#/$defs/Command"
            },
            {
              "type": "null"
            }
          ]
        },
        "email": {
          "anyOf": [
            {
              "$ref": "#/$defs/Email"
            },
            {
              "type": "null"
            }
          ]
        },
        "before_blob": {
          "type": [
            "string",
            "null"
          ]
        },
        "after_blob": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "additionalProperties": false,
      "required": [
        "type"
      ]
    },
    "PreviewType": {
      "type": "string",
      "enum": [
        "text",
        "markdown",
        "code",
        "diff",
        "files",
        "command",
        "json",
        "html",
        "image",
        "video",
        "audio",
        "file",
        "pdf",
        "email"
      ]
    },
    "PreviewFile": {
      "type": "object",
      "properties": {
        "path": {
          "type": "string"
        },
        "blob": {
          "type": "string"
        },
        "mime": {
          "type": "string"
        },
        "size": {
          "type": "integer",
          "format": "uint64",
          "minimum": 0
        },
        "lang": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        }
      },
      "additionalProperties": false,
      "required": [
        "path",
        "blob",
        "mime",
        "size"
      ]
    },
    "Command": {
      "type": "object",
      "properties": {
        "shell": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "argv": {
          "type": [
            "array",
            "null"
          ],
          "items": {
            "type": "string"
          },
          "default": null
        },
        "cwd": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "env_keys": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": []
        }
      },
      "additionalProperties": false
    },
    "Email": {
      "description": "An email draft for review. handup never sends it: on approval the agent\nsends the draft (or the edited `decision.fields`) with its own mail tool.",
      "type": "object",
      "properties": {
        "from": {
          "type": [
            "string",
            "null"
          ]
        },
        "to": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "cc": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "bcc": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "reply_to": {
          "type": [
            "string",
            "null"
          ]
        },
        "subject": {
          "type": "string",
          "default": ""
        },
        "date": {
          "description": "Date shown in the header, as the agent formats it (e.g. RFC 2822).",
          "type": [
            "string",
            "null"
          ]
        },
        "body": {
          "description": "Message body as Markdown (plain text renders as-is).",
          "type": "string",
          "default": ""
        },
        "body_html": {
          "description": "Optional HTML alternative; rendered only in the sandboxed HTML preview frame.",
          "type": [
            "string",
            "null"
          ]
        },
        "attachments": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/EmailAttachment"
          }
        },
        "in_reply_to": {
          "description": "The message this draft replies to, shown collapsed under the body.",
          "anyOf": [
            {
              "$ref": "#/$defs/EmailQuote"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "additionalProperties": false
    },
    "EmailAttachment": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "mime": {
          "type": [
            "string",
            "null"
          ]
        },
        "size": {
          "type": [
            "integer",
            "null"
          ],
          "format": "uint64",
          "minimum": 0
        },
        "blob": {
          "description": "Uploaded blob hash (`POST /v1/blobs`) so the reviewer can open the file.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "additionalProperties": false,
      "required": [
        "name"
      ]
    },
    "EmailQuote": {
      "type": "object",
      "properties": {
        "from": {
          "type": [
            "string",
            "null"
          ]
        },
        "date": {
          "type": [
            "string",
            "null"
          ]
        },
        "body": {
          "description": "Plain text of the earlier message (or a snippet of it).",
          "type": "string",
          "default": ""
        }
      },
      "additionalProperties": false
    },
    "Decision": {
      "type": "object",
      "properties": {
        "option": {
          "type": "string"
        },
        "feedback": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "fields": {
          "anyOf": [
            {
              "$ref": "#/$defs/DecisionFields"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "content_hash": {
          "type": "string"
        },
        "decided_by": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "decided_at": {
          "type": [
            "integer",
            "null"
          ],
          "format": "uint64",
          "minimum": 0,
          "default": null
        },
        "outcome": {
          "anyOf": [
            {
              "$ref": "#/$defs/Outcome"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "rule_id": {
          "type": [
            "string",
            "null"
          ]
        },
        "scope": {
          "type": [
            "string",
            "null"
          ]
        },
        "run_result": {
          "description": "The desktop app ran the request's command for the human: this is how\nit ended. Present only on approved command requests decided by Run.",
          "anyOf": [
            {
              "$ref": "#/$defs/RunResult"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "additionalProperties": false,
      "required": [
        "option",
        "content_hash"
      ]
    },
    "DecisionFields": {
      "description": "Decision `fields`. Question requests carry only `answers`, keyed by question\nid; other requests carry answers to `input.fields` or edited tool input.",
      "type": "object",
      "properties": {
        "answers": {
          "type": [
            "object",
            "null"
          ],
          "additionalProperties": {
            "$ref": "#/$defs/Answer"
          }
        }
      }
    },
    "Answer": {
      "description": "The human's answer to one question.",
      "type": "object",
      "properties": {
        "selected": {
          "description": "Chosen option labels; at most one unless the question is `multi`.",
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": []
        },
        "text": {
          "description": "Typed answer; only when the question allows free text.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "additionalProperties": false
    },
    "Outcome": {
      "type": "string",
      "enum": [
        "approve",
        "deny"
      ]
    },
    "RunResult": {
      "description": "Outcome of running a command request in the handup desktop app. The\ncommand already ran: an agent must not run it again. Output is the\nredacted tail of each stream.",
      "type": "object",
      "properties": {
        "exit_code": {
          "description": "Process exit status; absent when it never started or a signal ended it.",
          "type": [
            "integer",
            "null"
          ],
          "format": "int32"
        },
        "stdout_tail": {
          "description": "Last bytes of standard output (at most 64 KiB), credentials redacted.",
          "type": "string",
          "default": ""
        },
        "stderr_tail": {
          "description": "Last bytes of standard error (at most 64 KiB), credentials redacted.",
          "type": "string",
          "default": ""
        },
        "duration_ms": {
          "description": "Wall time from start to exit, in milliseconds.",
          "type": "integer",
          "format": "uint64",
          "minimum": 0,
          "default": 0
        },
        "truncated": {
          "description": "Earlier output was dropped from either tail.",
          "type": "boolean",
          "default": false
        },
        "elevated": {
          "description": "Ran with administrator rights (pkexec on Linux, osascript on macOS).",
          "type": "boolean",
          "default": false
        },
        "error": {
          "description": "Why it did not finish normally: timeout, cancellation, signal, or a\nstart failure (for example pkexec missing or authentication refused).",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "additionalProperties": false
    }
  }
}
```
