Skip to content

Surveys and Intake

Eric Fitzgerald edited this page Oct 18, 2026 · 2 revisions

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.

Endpoints

Survey administration (/admin/surveys)

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

Intake (/intake)

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

Triage (/triage)

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

Surveys

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: active surveys appear in intake; inactive surveys are hidden but still editable; archived surveys are read-only and kept for history.
  • Settings: allow_threat_model_linking controls 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.

Survey responses

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.

Statuses and transitions

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 /status to submitted (allowed while the response is draft or needs_revision; otherwise 409). Through intake endpoints a requester can set /status only to draft or submitted; any other target returns 400 invalid_input, so a requester cannot approve their own response. PUT and create carry no status.
  • A reviewer approves or returns with PATCH /triage/survey_responses/{survey_response_id} setting /status to ready_for_review or needs_revision. Moving to needs_revision requires revision_notes.
  • POST /triage/survey_responses/{survey_response_id}/create_threat_model requires the response to be ready_for_review (otherwise 409), and returns 409 if a threat model was already created. It creates the threat model, copies answers into its metadata, makes the response owner the threat model owner, stores created_threat_model_id, and moves the response to review_created. The response contains threat_model_id and survey_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).

Authorization

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 access

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.

Webhook events

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.

Related pages

Home

Releases


Getting Started

Deployment

Operation

Troubleshooting

Development

Integrations

Tools

API Reference

Reference

Clone this wiki locally