Repository navigation
Surveys and Intake
TMI's intake workflow lets teams request a security review by filling out an administrator-defined survey. Security reviewers then triage the submitted responses and create threat models from them. Intake is optional; threat models can be created without it.
There are three personas:
- Administrators create and manage surveys.
- Requesters (software developers and others) fill out surveys. Their answers are a survey response.
- Security reviewers triage responses and create threat models from them.
The OpenAPI specification is the authoritative reference for fields and status codes; see REST-API-Reference and API-Specifications.
| Method | Path | Description |
|---|---|---|
| GET | /admin/surveys |
List surveys (paginated, all statuses) |
| POST | /admin/surveys |
Create a survey |
| GET | /admin/surveys/{survey_id} |
Get a survey |
| PUT | /admin/surveys/{survey_id} |
Replace a survey |
| PATCH | /admin/surveys/{survey_id} |
Partially update a survey (JSON Patch), for example to change its status |
| DELETE | /admin/surveys/{survey_id} |
Delete a survey; a survey that has responses cannot be deleted (409) |
| GET, POST, PUT, DELETE | /admin/surveys/{survey_id}/metadata[/{key}] |
Survey metadata, plus /metadata/bulk
|
| Method | Path | Description |
|---|---|---|
| GET | /intake/surveys |
List active surveys |
| GET | /intake/surveys/{survey_id} |
Get a survey to fill out |
| POST | /intake/survey_responses |
Create a response (starts as draft) |
| GET | /intake/survey_responses |
List the caller's responses |
| GET | /intake/survey_responses/{survey_response_id} |
Get a response |
| PUT | /intake/survey_responses/{survey_response_id} |
Replace a response (only while draft or needs_revision) |
| PATCH | /intake/survey_responses/{survey_response_id} |
Partially update a response (JSON Patch); this is also how a response is submitted |
| DELETE | /intake/survey_responses/{survey_response_id} |
Delete a response (owner only) |
| GET | /intake/survey_responses/{survey_response_id}/triage_notes[/{triage_note_id}] |
Read triage notes (read-only for requesters) |
| GET, POST, PUT, DELETE | /intake/survey_responses/{survey_response_id}/metadata[/{key}] |
Response metadata, plus /metadata/bulk
|
| Method | Path | Description |
|---|---|---|
| GET | /triage/survey_responses |
List responses for triage; filter with ?status=submitted for the queue |
| GET | /triage/survey_responses/{survey_response_id} |
Get a response |
| PATCH | /triage/survey_responses/{survey_response_id} |
Change the response status (JSON Patch on /status) |
| POST | /triage/survey_responses/{survey_response_id}/create_threat_model |
Create a threat model from the response |
| GET, POST | /triage/survey_responses/{survey_response_id}/triage_notes |
List and add triage notes |
| GET | /triage/survey_responses/{survey_response_id}/metadata[/{key}] |
Read response metadata |
A survey has a name, a version string chosen by the administrator (for example 2024-Q1), a status, settings, and a survey_json document that defines the questions (SurveyJS format).
-
Status:
activesurveys appear in intake;inactivesurveys are hidden but still editable;archivedsurveys are read-only and kept for history. -
Settings:
allow_threat_model_linkingcontrols whether responses may link to an existing threat model (for re-reviews). - A response records the survey version it was created against (
survey_version) and completes against that version. Deactivating a survey does not block existing drafts.
A response belongs to one survey (survey_id) and holds answers, an optional linked_threat_model_id, an optional project_id, an authorization list, and the fields the server manages: status, owner, is_confidential, revision_notes, created_threat_model_id, submitted_at, reviewed_at and reviewed_by.
is_confidential can be set only when the response is created. After that it is read-only.
Responses have five statuses:
| Status | Meaning |
|---|---|
draft |
Being filled out by the requester |
submitted |
Waiting in the triage queue |
needs_revision |
Returned to the requester; revision_notes explain why |
ready_for_review |
Approved by a reviewer; a threat model can be created |
review_created |
A threat model has been created from the response |
Intended flow:
draft --submit--> submitted --approve--> ready_for_review --create_threat_model--> review_created
| |
+--return--> needs_revision <--return--+
needs_revision --submit--> submitted
There are no separate submit, approve or return endpoints. Every transition is a PATCH that sets /status:
- A requester submits with
PATCH /intake/survey_responses/{survey_response_id}setting/statustosubmitted(allowed while the response isdraftorneeds_revision; otherwise409). Through intake endpoints a requester can set/statusonly todraftorsubmitted; any other target returns400 invalid_input, so a requester cannot approve their own response.PUTand create carry no status. - A reviewer approves or returns with
PATCH /triage/survey_responses/{survey_response_id}setting/statustoready_for_revieworneeds_revision. Moving toneeds_revisionrequiresrevision_notes. -
POST /triage/survey_responses/{survey_response_id}/create_threat_modelrequires the response to beready_for_review(otherwise409), and returns409if a threat model was already created. It creates the threat model, copies answers into its metadata, makes the response owner the threat model owner, storescreated_threat_model_id, and moves the response toreview_created. The response containsthreat_model_idandsurvey_response_id.
The server checks that a status value is one of the five above (an unknown value or a missing revision_notes returns 400 invalid_input), and intake callers are limited to draft and submitted, but the server does not enforce the order of transitions. Treat the flow above as the intended process.
Updates use optimistic locking; send If-Match with the version from the last read (see API-Overview).
Surveys are administrator-managed and have no access control list. All authenticated users can list and read active surveys through /intake/surveys.
Survey responses use the same authorization-list model as threat models (see Architecture-and-Design): the creator is the owner and the authorization list grants other users or groups reader, writer or owner access. Intake endpoints use this list:
| Role | View | Edit (while draft or needs_revision) |
Submit | Delete |
|---|---|---|---|---|
| Owner | Yes | Yes | Yes | Yes |
| Writer | Yes | Yes | Yes | No |
| Reader | Yes | No | No | No |
Triage endpoints are open only to reviewer-group members, and ownership grants no triage access:
- Members of Security Reviewers can triage non-confidential responses.
- Members of Confidential Project Reviewers can triage confidential responses.
- Members of both groups can triage all responses.
- There is no administrator bypass; administrators need the group membership like anyone else.
A caller without the matching group gets 404 on every per-response triage endpoint (the same as for a missing response), and an empty list from GET /triage/survey_responses. A requester therefore reaches their own response only through the intake endpoints.
When a response is created, the matching reviewer group is added to its authorization list with the owner role: Security Reviewers for a non-confidential response, Confidential Project Reviewers for a confidential one. Group membership comes from your identity provider's groups or, when auth.everyone_is_a_reviewer is enabled, from all users. See Configuration-Reference.
Survey and survey response changes emit webhook events (survey_response.created, survey_response.updated, survey_response.deleted, and the survey.* equivalents); see Webhook-Integration for the event list.
- Using TMI for Threat Modeling
- Accessing TMI
- Authentication
- Identity Linking
- Creating Your First Threat Model
- Surveys and Intake
- Understanding the User Interface
- Working with Data Flow Diagrams
- Managing Threats
- Collaborative Threat Modeling
- Using Notes and Documentation
- Timmy AI Assistant
- Metadata and Extensions
- Planning Your Deployment
- Terraform Deployment (AWS)
- Deploying TMI Server
- Oracle Container Images
- Deploying TMI Web Application
- Setting Up Authentication
- Database Setup
- Bootstrapping Production
- Component Integration
- Post-Deployment
- Branding and Customization
- Monitoring and Health
- Cloud Logging
- Configuring Local Development
- Managing Operational Settings
- Content Extractors - Limits and Overrides
- Database Operations
- Database Security Strategies
- Transaction Isolation
- Oracle Content Feedback FK Cleanup
- Security Operations
- Secret Rotation
- Performance and Scaling
- Maintenance Tasks
- Getting Started with Development
- Local Development Cluster
- Architecture and Design
- API Integration
- Testing
- Contributing
- Extending TMI
- Dependency Upgrade Plans
- DFD Graphing Library Reference
- Migration Instructions
- Issue Tracker Integration
- Webhook Integration
- Addon System
- MCP Integration
- Delegated Content Providers
- Setting Up Google Content Providers
- API Clients
- API Client Maintenance
- Database Tool Reference
- TMI Terraform Analyzer
- WebSocket Test Harness