Owlmetry
CLI

Questionnaires

Create, edit, and review in-app questionnaires from the Owlmetry CLI.

Questionnaires are structured multi-question surveys shown in-app via the Swift SDK's OwlQuestionnaireView or the auto-trigger .owlQuestionnaire(slug:trigger:…) view modifier. The auto-trigger opens at a small "Have a minute for feedback?" detent with three actions (take the survey, maybe later, never ask again) and expands to a step-through flow on consent — one question per page with a progress bar, non-swipe-dismissible so users finish or cancel deliberately. Each questionnaire has an immutable slug, a versioned schema of up to 30 questions (text, single/multi choice, rating, NPS), and a one-and-done global dismiss path so users who reject are never asked again. Responses store a snapshot of the schema they were submitted against, so historical answers always render correctly even after the parent definition has been edited.

Drafts are first-class responses. The SDK saves the current answer set on every Next tap (not just on Submit), so a user who quits halfway leaves a row with submitted_at = null and status = 'draft'. The next eligible launch resumes them at the first unanswered question with prior answers pre-filled (consent is skipped on resume). The final Submit flips submitted_at to non-null, snapshots the schema onto the row, transitions status to new, and fires the team notification exactly once. Per-question analytics and response lists include drafts by default so abandonment shows up naturally as a drop-off curve; pass submitted_only to scope to completed submissions. Abandoned drafts untouched for 90 days are soft-deleted by the daily questionnaire_draft_cleanup job.

The owlmetry questionnaires command group manages definitions and reads responses.

Create a questionnaire

owlmetry questionnaires create \
  --project-id <id> \
  --slug post-onboarding \
  --name "Onboarding survey" \
  --schema-file ./onboarding-survey.json

The --schema-file flag points at a JSON file shaped like { version: 1, questions: [...] }. You can also pass --schema '<json>' inline.

A minimal schema file:

{
  "version": 1,
  "questions": [
    {
      "id": "q_rating",
      "type": "rating",
      "title": "How would you rate the app?",
      "required": true,
      "scale": 5
    },
    {
      "id": "q_role",
      "type": "single_choice",
      "title": "How would you describe yourself?",
      "required": true,
      "options": [
        { "id": "hobby", "label": "Hobbyist" },
        { "id": "indie", "label": "Indie developer" },
        { "id": "team", "label": "Part of a team" }
      ]
    },
    {
      "id": "q_feedback",
      "type": "text",
      "title": "Anything you'd like to add?",
      "required": false,
      "multiline": true
    }
  ]
}

Slug must be lowercase letters/digits/hyphens (max 64 chars) and is immutable after creation.

text questions default to a single-line field. Set "multiline": true (as on q_feedback above) to render a tall text editor in the SDK — use it for paragraph-style answers. Both variants share the same 4000-character cap.

List questionnaires

# Single project
owlmetry questionnaires list --project-id <id>

# Every questionnaire across every accessible project in a team
owlmetry questionnaires list --team-id <id>
FlagDescription
--project-id <id>Project ID (mutually exclusive with --team-id)
--team-id <id>Team ID — lists across every accessible project (mutually exclusive with --project-id). Adds a Project column to the table output.
--app-id <id>Filter to a specific app (only with --project-id)
--activeShow only active questionnaires
--inactiveShow only paused questionnaires
--data-mode <m>production, development, all — filters the rolled-up response_count / submitted_count / last_response_at on each row. Default: all modes.
--limit <n>Max entries
--cursor <cursor>Pagination cursor (only with --project-id; team mode is LIMIT-capped)

View definition

owlmetry questionnaires view <questionnaireId> --project-id <id>

Prints the schema, response count, last-response timestamp. Pass --data-mode <production|development|all> to filter the rolled-up response_count / submitted_count / last_response_at (default: all modes mixed).

Update

# Pause a questionnaire so the SDK stops returning it as eligible
owlmetry questionnaires update <id> --project-id <pid> --active false

# Replace the schema (slug stays the same — existing responses keep their snapshot)
owlmetry questionnaires update <id> --project-id <pid> --schema-file ./v2-schema.json

# Pin to a specific app
owlmetry questionnaires update <id> --project-id <pid> --app-id <app-id>

Slug cannot be updated. Passing --description "" clears the description.

Delete

owlmetry questionnaires delete <id> --project-id <pid>

User-only. Agent keys get 403. Existing responses are preserved (soft delete sets deleted_at and is_active=false).

List responses

owlmetry questionnaires responses <questionnaireId> --project-id <id>

Drafts and submitted responses are both listed by default — drafts carry status: "draft" and submitted_at: null. Pass --submitted-only to scope to completed submissions, or --status draft to scope to in-progress drafts only.

FlagDescription
--status <status>Filter: draft, new, in_review, addressed, dismissed
--submitted-onlyHide drafts; show only fully-submitted responses
--app-id <id>Filter to a specific app
--devDev responses only
--data-mode <m>production (default), development, all
--limit <n>Max entries
--cursor <cursor>Pagination cursor

View a response

owlmetry questionnaires response <responseId> --project-id <pid> --questionnaire <qid>

Renders each question (from the response's frozen schema_snapshot) alongside the answer plus any comments.

Update response status

owlmetry questionnaires status <responseId> \
  --project-id <pid> --questionnaire <qid> --to in_review

Target status accepts draft, new, in_review, addressed, or dismissed. A draft normally transitions to new automatically when the user taps Submit in the SDK; you'd typically only set --to draft to undo a triage transition while the row is still unsubmitted.

Add a comment

owlmetry questionnaires comment <responseId> \
  --project-id <pid> --questionnaire <qid> \
  --body "Cross-linked to issue #321"

Analytics

owlmetry questionnaires analytics <questionnaireId> --project-id <id>

Pre-aggregated distribution per question:

  • text → 10 most recent answers
  • single_choice / multi_choice → bar list with counts + %
  • rating → 1–5 buckets + average
  • nps → 0–10 buckets + score + detractor/passive/promoter split

Drafts contribute to the rollups by default — a Q1-only draft adds to Q1's count and naturally drops out of Q2+, so abandonment shows up as a drop-off curve across the question list. Pass --submitted-only to compute rollups against fully-submitted responses only. The summary line always surfaces submitted_count of total_responses so the "M of N completed" headline renders either way.

FlagDescription
--submitted-onlyCompute rollups against fully-submitted responses only
--devDev responses only
--data-mode <m>production, development, all

Ready to get started?

Connect your agent via MCP or CLI and start tracking.