empirio.ai Developer Docs

empirio.ai offers four integration surfaces:

  • MCP Server — connect AI agents (ChatGPT, Claude, and compatible hosts). Available on every plan, including Free.
  • REST API — manage surveys and pull responses over HTTPS. Requires a Pro or Enterprise plan.
  • Webhooks — receive new responses in real time. Requires a Pro or Enterprise plan.
  • CLI — @empirio-ai/cli npm package; OAuth 2.1 login and every REST operation on the command line. Requires a Pro or Enterprise plan.

Beta — This API is in beta. Endpoints, request/response shapes, and behavior may change without prior notice.

REST API

Overview

Use the REST API to manage surveys and read response data over HTTPS.

Base URL

https://platform.empirio.ai/rest/v1

All requests must be made over HTTPS. HTTP requests are rejected with a 308 redirect.

Authentication

Include a Bearer token in every request. Two token types are accepted:

Authorization: Bearer sk_live_…        # REST API key (dashboard)
Authorization: Bearer <oauth-token>    # OAuth 2.1 access token (used by the CLI)

API keys are created in Settings → Integrations. OAuth access tokens are issued via the /oauth/token endpoint and are used automatically by the empirio CLI after empirio login. Both token types carry one or both scopes:

ScopeAccess
surveysCreate, read, edit, delete, publish, unpublish, duplicate surveys
responsesList, aggregate, export, delete responses; cross-tabulation; chart export

The GET /me endpoint is exempt from the scope and plan gates so any authenticated caller can inspect their own identity and plan tier.

Rate limits

MetricLimit
Read operations60 / minute per key
Write operations20 / minute per key
Pre-auth (per IP)30 / minute
POST /surveys (survey_create)10 / hour and 50 / day per account

When a limit is exceeded the API returns 429 with a Retry-After header indicating seconds until the window resets.

Write operations include POST, PATCH, and DELETE. Read operations are GET requests.

The per-account survey_create limit is applied in addition to the global write rate limit and is intended to prevent account-wide runaway creation. It is not affected by idempotency replays — re-sending a request with the same Idempotency-Key does not consume additional quota.

Admin exemption

API keys owned by accounts with the admin role bypass every per-key and per-account rate limit (read, write, and the survey_create hourly/daily caps). The pre-auth IP limit still applies because admin status is only known after the API key is validated. This exemption is intended for internal tooling and test automation — regular Pro/Enterprise accounts remain subject to the limits documented above.

Idempotency

Write operations require an idempotency key via header:

  • Header: Idempotency-Key: <value> (max 128 characters)

Repeat requests with the same key within the idempotency window return the original response without re-executing the operation.

Error responses

All errors follow a consistent envelope:

{
  "ok": false,
  "error": {
    "code": "not_found",
    "message": "Survey not found."
  }
}
CodeStatusDescription
not_authorized401Missing or invalid API key
insufficient_scope403Key does not have the required scope
plan_required403Account requires Pro or Enterprise plan
forbidden403Action not permitted
not_found404Resource or route not found
method_not_allowed405HTTP method not allowed for this endpoint
validation_error400Request parameters failed validation
idempotency_required400Idempotency key required but not provided
idempotency_in_progress409Duplicate request is still being processed
rate_limited429Rate limit exceeded
internal_error500Unexpected server error

Recommended survey flow

  1. POST /surveys — create a draft
  2. PATCH /surveys/{survey_id} — optional iterative edits
  3. POST /surveys/{survey_id}/publish — publish the draft

Question Types

Write endpoints accept questions as an array of objects. Every question needs a type and question text plus any type-specific fields listed below. The backend assigns internal IDs automatically — clients only send labels.

Selection
Single choice — multiple-choice
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
optionsstring[]NoAnswer labels (max 100)
randomizeOptionsbooleanNoShuffle option order per respondent
allowOtherbooleanNoAllow a free-text "other" answer
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "multiple-choice",
  "question": "How did you hear about us?",
  "options": ["Social media", "Search engine", "Friend"],
  "allowOther": true
}
Checkboxes — checkbox
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
optionsstring[]NoAnswer labels (max 100)
randomizeOptionsbooleanNoShuffle option order per respondent
allowOtherbooleanNoAllow a free-text "other" answer
minSelectionsintegerNoMinimum options the respondent must select (≥ 1). Implies the question is required when set.
maxSelectionsintegerNoMaximum options the respondent may select (≥ 1, ≥ minSelections, ≤ options.length).
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "checkbox",
  "question": "Which features do you use?",
  "options": ["Dashboard", "Reports", "API"],
  "randomizeOptions": true,
  "minSelections": 1,
  "maxSelections": 2
}
Dropdown — dropdown
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
optionsstring[]NoAnswer labels (max 100)
randomizeOptionsbooleanNoShuffle option order per respondent
allowOtherbooleanNoAllow a free-text "other" answer
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "dropdown",
  "question": "Select your country",
  "options": ["Germany", "Austria", "Switzerland"]
}
Yes / No — yes-no
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "yes-no",
  "question": "Would you use this product again?",
  "required": true
}
Rating
Star rating — rating
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
minnumberNoFixed at 1
maxnumberNo2–10 (default 5)
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "rating",
  "question": "How would you rate our service?",
  "required": true,
  "min": 1,
  "max": 5
}
Thumbs — thumbs
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
maxnumberNo2–10 (default 5)
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "thumbs",
  "question": "Did you enjoy this experience?",
  "required": true,
  "max": 5
}
Number rating — scale
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
minnumberNoFixed at 1
maxnumberNo2–20 (default 10)
scaleLabels{ min?: string, max?: string }NoLabel texts for scale ends
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "scale",
  "question": "How satisfied are you?",
  "min": 1,
  "max": 10,
  "scaleLabels": {
    "min": "Not at all",
    "max": "Extremely"
  }
}
Text rating — text-rating
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
matrixColumnsstring[]NoLabel texts shown on each rating button
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "text-rating",
  "question": "How do you feel?",
  "matrixColumns": ["Bad", "Neutral", "Good", "Great"]
}
NPS — nps
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "nps",
  "question": "How likely are you to recommend us?",
  "required": true
}
Text & Input
Short text — text
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "text",
  "question": "What is your name?",
  "required": true
}
Long text — text-long
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "text-long",
  "question": "Please share additional feedback"
}
Email — email
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "email",
  "question": "Your email address",
  "required": true
}
Phone — phone
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "phone",
  "question": "Your contact number"
}
Number — number
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "number",
  "question": "How many employees does your company have?",
  "required": true
}
Date — date
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "date",
  "question": "When did you first use our product?"
}
Advanced
Matrix — matrix
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
matrixRowsstring[]NoRow labels
matrixColumnsstring[]NoColumn labels
randomizeRowsbooleanNoShuffle row order per respondent
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "matrix",
  "question": "Rate each feature",
  "matrixRows": ["Ease of use", "Performance", "Design"],
  "matrixColumns": ["Poor", "Fair", "Good", "Excellent"]
}
Ranking — ranking
FieldTypeRequiredNotes
questionstringYesQuestion text
requiredbooleanNoWhether an answer is required
optionsstring[]NoItems to rank
randomizeOptionsbooleanNoShuffle option order per respondent
subtitlestringNoOptional subtitle text
showSubtitlebooleanNoWhether to display the subtitle
{
  "type": "ranking",
  "question": "Rank by importance",
  "options": ["Speed", "Reliability", "Price", "Support"]
}
Content — content
FieldTypeRequiredNotes
questionstringYesTitle / label
contentstringNoBody text
{
  "type": "content",
  "question": "Section 2: Demographics",
  "content": "This section collects demographic information."
}
Privacy Policy — privacy
FieldTypeRequiredNotes
questionstringYesPolicy title
requiredbooleanNoWhether consent is required to proceed
contentstringNoPolicy text shown to the respondent
privacyCheckboxLabelstringNoLabel displayed next to the consent checkbox
{
  "type": "privacy",
  "question": "Privacy Policy",
  "content": "I agree to the processing of my data.",
  "privacyCheckboxLabel": "I accept",
  "required": true
}

Surveys

Create, edit, publish, duplicate, and delete surveys.

get/surveys

List surveys

List all surveys owned by or shared with the authenticated user.

Parameters
NameInTypeRequiredDescription
statusquery"all" | "draft" | "published"NoFilter by publication status
limitqueryintegerNoMax results to return (default 50)
offsetqueryintegerNoNumber of results to skip (default 0)
Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

  • surveys array of objectrequired
    • id string (uuid)required

      Survey ID (UUID)

    • title stringrequired
    • description string (nullable)required
    • is_published booleanrequired
    • role "owner" | "editor" | "viewer"required
    • response_count integerrequired
    • created_at stringrequired

      ISO 8601 timestamp

    • updated_at stringrequired

      ISO 8601 timestamp

  • total integerrequired

    Total number of accessible surveys, ignoring pagination.

Example response

{
  "surveys": [
    {
      "id": "9f3a8b12-4c5d-4e6f-8a1b-0c2d3e4f5a6b",
      "title": "Customer Satisfaction Q1",
      "description": "Quick 3-minute check-in with our Pro customers.",
      "is_published": true,
      "role": "owner",
      "response_count": 142,
      "created_at": "2026-01-12T09:24:00.000Z",
      "updated_at": "2026-04-02T16:03:18.000Z"
    },
    {
      "id": "c1b2a3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "title": "Onboarding feedback",
      "description": null,
      "is_published": false,
      "role": "editor",
      "response_count": 0,
      "created_at": "2026-03-20T11:00:00.000Z",
      "updated_at": "2026-03-21T08:30:00.000Z"
    }
  ],
  "total": 2
}

post/surveys

Create survey draft

Create a new survey draft (unpublished). Questions are provided as an array — each needs a type and question text plus type-specific fields. See the Question Types section for the full list of supported types and their fields.

All field-level details are documented directly in the request schema attributes.

Logic rules and translations require question_id references — use survey_edit after creation to add them.

Parameters
NameInTypeRequiredDescription
Idempotency-KeyheaderstringYesRequired idempotency key for write operations. Reuse the same value when retrying the same request.
Request body

Required

  • mode "manual" | "ai"required

    Creation mode: 'manual' for explicit content, 'ai' for prompt-based generation

  • questions array of object

    Array of question objects (each must have a valid type)

    • type "rating" | "thumbs" | "text-rating" | "text" | "text-long" | "nps" | "multiple-choice" | "checkbox" | "dropdown" | "yes-no" | "scale" | "date" | "email" | "phone" | "number" | "ranking" | "matrix" | "content" | "privacy"required

      Question type.

    • question string

      Question text.

    • required boolean

      Whether an answer is required.

    • options array of string

      Answer option labels (max 100).

    • subtitle string

      Optional subtitle.

    • showSubtitle boolean

      Whether the subtitle is shown.

    • content string

      Body content for content/privacy questions.

    • min 1

      Lower bound. Only supported by rating and scale, where it is always 1. Ignored for other types.

    • max integer

      Upper bound. rating/thumbs: 2–10, scale: 2–20. Ignored for types without max support.

    • scaleLabels object

      Scale endpoint labels.

      • min string

        Minimum scale label.

      • max string

        Maximum scale label.

    • allowOther boolean

      Allow an additional free-text 'other' answer.

    • randomizeOptions boolean

      Randomize option order for each respondent.

    • minSelections integer

      Minimum number of options the respondent must select. checkbox-only. Implies the question is required when ≥ 1.

    • maxSelections integer

      Maximum number of options the respondent may select. checkbox-only. Must be ≥ minSelections (if set) and ≤ options.length.

    • matrixRows array of string

      Matrix row labels.

    • matrixColumns array of string

      Matrix column labels.

    • randomizeRows boolean

      Randomize matrix row order for each respondent.

    • privacyCheckboxLabel string

      Label shown next to the privacy consent checkbox.

    • translations map of object

      Per-locale question translations keyed by locale (for example `de-DE`).

  • ai_prompt string

    Prompt text for AI-driven survey creation (ai mode)

  • metadata object

    Survey metadata object. Field-level descriptions define title, description, and display mode.

    • title string

      Survey title shown to respondents and in the dashboard.

    • description string

      Optional survey description shown in intro contexts.

    • display_mode "all" | "single"

      Question rendering mode: `all` shows all questions on one page; `single` shows one question per page.

  • welcome_page object

    Welcome page configuration shown before the first question.

    • enabled booleanrequired

      Whether the welcome page is shown before the first question.

    • title string

      Welcome page title.

    • description string

      Welcome page description text.

  • end_page object

    End page configuration shown after the last question.

    • enabled booleanrequired

      Whether the end page is shown after the final question.

    • title string

      Custom end-page title. If omitted, the default title is used.

    • description string

      Custom end-page description text. If omitted, the default text is used.

    • show_title boolean

      Explicit title visibility toggle. Use `false` to hide the title even if `title` is set.

    • show_description boolean

      Explicit description visibility toggle. Use `false` to hide the description even if it exists.

    • show_button boolean

      Whether to show a call-to-action button on the end page.

    • button_text string

      Button label shown on the end page.

    • button_url string

      Destination URL opened by the button and used as redirect target when `auto_redirect` is enabled.

    • auto_redirect boolean

      When `true`, respondents are redirected automatically to `button_url` after `redirect_delay`.

    • redirect_delay string

      Auto-redirect delay as a duration-like string.

  • design object

    Design customization settings for survey styling (for example template and primary color).

    • template "standard" | "modern" | "cosmos" | "aurora" | "nebula" | "eclipse" | "forest" | "amber"

      Visual survey template. Available: `standard` (light, default), `modern` (dark), `cosmos` (dark, depth), `aurora` (light, gradient), `nebula` (dark, purple), `eclipse` (dark, purple accent), `forest` (dark, green), `amber` (dark, blue).

    • primary_color string

      Primary accent color as hex (for example `#4F46E5`).

  • settings object

    Survey behavior settings. Field-level descriptions define the supported allowlisted keys.

    • show_progress boolean

      Whether to show a progress indicator during completion.

    • show_question_numbers boolean

      Whether to show numeric question labels (1, 2, 3...).

    • auto_advance boolean

      Whether the respondent flow automatically advances after a question is answered when the question type supports it. Defaults to true.

    • hide_branding boolean

      Whether to hide empirio branding in the survey footer.

    • master_locale string

      Primary survey locale in BCP 47 format (for example `en-US`, `de-DE`). Defaults to the user's preferred locale when omitted during creation.

    • multi_language_enabled boolean

      Whether multi-language answering is enabled.

    • additional_locales array of string

      Additional enabled locales in BCP 47 format.

Example — Creates a survey with common question types: rating, multiple-choice, NPS, and free text.

{
  "mode": "manual",
  "metadata": {
    "title": "Customer Feedback Survey",
    "description": "Help us improve our service"
  },
  "settings": {
    "master_locale": "en-US"
  },
  "questions": [
    {
      "type": "rating",
      "question": "How would you rate our service?",
      "required": true,
      "min": 1,
      "max": 5
    },
    {
      "type": "multiple-choice",
      "question": "How did you hear about us?",
      "options": [
        "Social media",
        "Friend or colleague",
        "Search engine",
        "Advertisement"
      ],
      "allowOther": true
    },
    {
      "type": "nps",
      "question": "How likely are you to recommend us to a friend?",
      "required": true
    },
    {
      "type": "text-long",
      "question": "Any additional comments or suggestions?",
      "required": false
    }
  ]
}
Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

  • id string (uuid)

    Survey ID (UUID)

  • title string
  • is_published boolean
  • public_url string
  • public_url_note string
  • preview_url string

    Preview URL showing the current working draft. Responses submitted via this link are not counted.

Example response

{
  "id": "9f3a8b12-4c5d-4e6f-8a1b-0c2d3e4f5a6b",
  "title": "Customer Satisfaction Q1",
  "is_published": false,
  "public_url": "https://www.empirio.ai/s/9f3a8b12-4c5d-4e6f-8a1b-0c2d3e4f5a6b",
  "public_url_note": "Survey is not published yet. This link will only work after publishing."
}

get/surveys/{survey_id}

Get survey details

Get full details of a specific survey including questions and settings.

Options, matrixRows, and matrixColumns are returned as {option_id, label} objects. Use option_id to reference individual options in survey_edit option_operations.

Parameters
NameInTypeRequiredDescription
survey_idpathstring (uuid)YesThe survey ID
Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

  • id string (uuid)required

    Survey ID (UUID)

  • title stringrequired
  • description string (nullable)required
  • questions array of objectrequired
    • question_id string (nullable)required

      Stable question ID. `null` only for legacy rows without an assigned id.

    • type stringrequired

      Question type (e.g. text, multiple-choice, matrix, rating).

    • question stringrequired

      Question title (human-readable prompt).

    • required boolean
    • subtitle string
    • showSubtitle boolean
    • options array of object

      Answer options for selection questions. Each option has a stable option_id and display label.

      • option_id stringrequired

        Stable opaque option identifier. Use in option_operations to rename, delete, or reorder.

      • label stringrequired

        Display label for this option.

    • matrixRows array of object

      Matrix row options with stable identifiers.

      • option_id stringrequired

        Stable opaque option identifier. Use in option_operations to rename, delete, or reorder.

      • label stringrequired

        Display label for this option.

    • matrixColumns array of object

      Matrix column options with stable identifiers.

      • option_id stringrequired

        Stable opaque option identifier. Use in option_operations to rename, delete, or reorder.

      • label stringrequired

        Display label for this option.

    • min number
    • max number
    • scaleLabels object

      Optional labels for the min and max endpoints of a scale/rating question.

      • min string
      • max string
    • allowOther boolean
    • randomizeOptions boolean
    • randomizeRows boolean
    • maxSelections number
    • content string
    • privacyCheckboxLabel string
    • translations map of object

      Optional per-locale translation payload keyed by BCP-47 locale.

  • design_settings objectrequired

    Design + settings block. Individual fields depend on the survey template and which editor sections have been configured.

    • template string
    • displayMode "all" | "single"
    • primaryColor string
    • logicRules array of map of object
    • welcomePage map of object
    • endPage map of object
    • settings map of object
  • is_published booleanrequired
  • access_mode "open" | "invite_only" (nullable)required
  • role "owner" | "editor" | "viewer"required
  • public_url stringrequired

    Public survey URL.

  • preview_url string

    Preview URL showing the current working draft. Responses submitted via this link are not counted.

  • has_pending_draft_changes booleanrequired

    True when there are unpublished edits in the working draft.

  • created_at stringrequired

    ISO 8601 timestamp

  • updated_at stringrequired

    ISO 8601 timestamp

Example response

{
  "id": "9f3a8b12-4c5d-4e6f-8a1b-0c2d3e4f5a6b",
  "title": "Customer Satisfaction Q1",
  "description": "Quick 3-minute check-in with our Pro customers.",
  "questions": [
    {
      "question_id": "q-a3f8d1b2-4e5c-4c2a-9d11-0f8b7e6a1c42",
      "type": "multiple-choice",
      "question": "Which product area do you use most?",
      "required": true,
      "options": [
        {
          "option_id": "opt_a1b2c3d4",
          "label": "Dashboard"
        },
        {
          "option_id": "opt_bbbb2222",
          "label": "Reports"
        },
        {
          "option_id": "opt_cccc3333",
          "label": "API"
        }
      ],
      "allowOther": true
    },
    {
      "question_id": "q-7b2f4e11-9c3a-4a1d-8e22-5f9d3b0a71e8",
      "type": "checkbox",
      "question": "Which features matter most to you?",
      "options": [
        {
          "option_id": "opt_dddd4444",
          "label": "Pricing"
        },
        {
          "option_id": "opt_eeee5555",
          "label": "Support"
        },
        {
          "option_id": "opt_ffff6666",
          "label": "Speed"
        }
      ],
      "randomizeOptions": true
    },
    {
      "question_id": "q-3f8d4b21-7a95-4c6e-89b1-2d5e7f1c9a34",
      "type": "rating",
      "question": "How would you rate our service?",
      "required": true,
      "min": 1,
      "max": 5
    },
    {
      "question_id": "q-6e2c9a74-5b38-4f1d-92e7-8a4c7b3d1f05",
      "type": "nps",
      "question": "How likely are you to recommend us?",
      "required": true
    }
  ],
  "design_settings": {
    "template": "standard",
    "displayMode": "single",
    "primaryColor": "#4F46E5",
    "logicRules": [],
    "welcomePage": {
      "enabled": true,
      "title": "Welcome!"
    },
    "endPage": {
      "enabled": true,
      "title": "Thank you!"
    },
    "settings": {
      "showProgress": true,
      "showQuestionNumbers": true,
      "autoAdvance": true,
      "surveyLocale": "en-US"
    }
  },
  "is_published": true,
  "access_mode": "open",
  "role": "owner",
  "public_url": "https://www.empirio.ai/s/9f3a8b12-4c5d-4e6f-8a1b-0c2d3e4f5a6b",
  "has_pending_draft_changes": false,
  "created_at": "2026-01-12T09:24:00.000Z",
  "updated_at": "2026-04-02T16:03:18.000Z"
}

patch/surveys/{survey_id}

Edit survey draft

Apply changes to a survey's working draft (does not publish). Include only the sections you want to change as top-level fields.

All field-level details and supported operation variants are documented directly in the request schema attributes.

See the request-body Examples for complete manual editing scenarios.

Parameters
NameInTypeRequiredDescription
survey_idpathstring (uuid)YesThe survey ID to edit
Idempotency-KeyheaderstringYesRequired idempotency key for write operations. Reuse the same value when retrying the same request.
Request body

Required

  • mode "manual" | "ai"required

    Edit mode: 'manual' applies provided changes, 'ai' generates changes from ai_prompt

  • question_operations array of object | object | object | object

    Question operations (`add`, `update`, `delete`, `reorder`).

  • option_operations array of object | object | object | object

    Option operations (`add`, `rename`, `delete`, `reorder`).

  • metadata object

    Partial metadata patch.

    • title string

      Survey title shown to respondents and in the dashboard.

    • description string

      Optional survey description shown in intro contexts.

    • display_mode "all" | "single"

      Question rendering mode: `all` shows all questions on one page; `single` shows one question per page.

  • welcome_page object

    Partial welcome-page patch.

    • enabled boolean

      Whether the welcome page is shown before the first question.

    • title string

      Welcome page title.

    • description string

      Welcome page description text.

  • end_page object

    Partial end-page patch.

    • enabled boolean

      Whether the end page is shown after the final question.

    • title string

      Custom end-page title. If omitted, the default title is used.

    • description string

      Custom end-page description text. If omitted, the default text is used.

    • show_title boolean

      Explicit title visibility toggle. Use `false` to hide the title even if `title` is set.

    • show_description boolean

      Explicit description visibility toggle. Use `false` to hide the description even if it exists.

    • show_button boolean

      Whether to show a call-to-action button on the end page.

    • button_text string

      Button label shown on the end page.

    • button_url string

      Destination URL opened by the button and used as redirect target when `auto_redirect` is enabled.

    • auto_redirect boolean

      When `true`, respondents are redirected automatically to `button_url` after `redirect_delay`.

    • redirect_delay string

      Auto-redirect delay as a duration-like string.

  • logic_rules array of object | object | object

    Logic-rule operations (`add`, `update`, `delete`).

  • design object

    Partial design patch.

    • template "standard" | "modern" | "cosmos" | "aurora" | "nebula" | "eclipse" | "forest" | "amber"

      Visual survey template. Available: `standard` (light, default), `modern` (dark), `cosmos` (dark, depth), `aurora` (light, gradient), `nebula` (dark, purple), `eclipse` (dark, purple accent), `forest` (dark, green), `amber` (dark, blue).

    • primary_color string

      Primary accent color as hex (for example `#4F46E5`).

  • settings object

    Partial settings patch.

    • show_progress boolean

      Whether to show a progress indicator during completion.

    • show_question_numbers boolean

      Whether to show numeric question labels (1, 2, 3...).

    • auto_advance boolean

      Whether the respondent flow automatically advances after a question is answered when the question type supports it. Defaults to true.

    • hide_branding boolean

      Whether to hide empirio branding in the survey footer.

    • master_locale string

      Primary survey locale in BCP 47 format (for example `en-US`, `de-DE`). Defaults to the user's preferred locale when omitted during creation.

    • multi_language_enabled boolean

      Whether multi-language answering is enabled.

    • additional_locales array of string

      Additional enabled locales in BCP 47 format.

  • translations object

    Translation patches for metadata and question content.

    • metadata map of object

      Per-locale metadata translation patches keyed by locale.

    • questions map of map of object

      Per-locale question translation patches keyed by locale then `question_id`.

  • ai_prompt string

    Prompt text for AI-driven survey editing (ai mode)

Example — Adds a dropdown question at the end of the survey.

{
  "mode": "manual",
  "question_operations": [
    {
      "op": "add",
      "position": {
        "type": "end"
      },
      "question": {
        "type": "dropdown",
        "question": "Select your department:",
        "options": [
          "Engineering",
          "Marketing",
          "Sales",
          "Support",
          "HR"
        ],
        "required": true
      }
    }
  ]
}
Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

  • id string (uuid)required

    Survey ID (UUID)

  • warnings array of string
  • applied_changes objectrequired

    Summary of what the edit actually changed. Added/updated/deleted entries carry `question_id` so callers can correlate applied changes against cached references.

    • question_changes objectrequired
      • added array of objectrequired
        • question_id string (nullable)required
        • question stringrequired
        • type stringrequired
        • position map of objectrequired
      • updated array of objectrequired
        • question_id stringrequired

          Stable opaque question identifier. Same value emitted by webhooks and returned by survey_get — safe to use as a join key across REST and webhook consumers.

        • changed_fields array of stringrequired
      • deleted array of objectrequired
        • question_id stringrequired

          Stable opaque question identifier. Same value emitted by webhooks and returned by survey_get — safe to use as a join key across REST and webhook consumers.

        • question stringrequired
        • type stringrequired
      • reordered booleanrequired
    • logic_rule_changes objectrequired
      • added integerrequired
      • updated integerrequired
      • deleted integerrequired
    • metadata_changes map of object
    • welcome_page_changes map of object
    • end_page_changes map of object
    • design_changes map of object
    • settings_changes map of object
    • translation_changes_summary map of object
    • option_changes object

      Summary of option-level changes applied by option_operations.

      • added array of objectrequired

        Options added (server-assigned option_id available via survey_get).

        • question_id stringrequired

          Stable opaque question identifier. Same value emitted by webhooks and returned by survey_get — safe to use as a join key across REST and webhook consumers.

        • label stringrequired
      • renamed array of objectrequired
        • question_id stringrequired

          Stable opaque question identifier. Same value emitted by webhooks and returned by survey_get — safe to use as a join key across REST and webhook consumers.

        • option_id stringrequired
        • label stringrequired
      • deleted array of objectrequired
        • question_id stringrequired

          Stable opaque question identifier. Same value emitted by webhooks and returned by survey_get — safe to use as a join key across REST and webhook consumers.

        • option_id stringrequired
      • reordered array of objectrequired
        • question_id stringrequired

          Stable opaque question identifier. Same value emitted by webhooks and returned by survey_get — safe to use as a join key across REST and webhook consumers.

        • field stringrequired

Example response

{
  "id": "9f3a8b12-4c5d-4e6f-8a1b-0c2d3e4f5a6b",
  "applied_changes": {
    "question_changes": {
      "added": [
        {
          "question_id": "q-new-b1c4d5e6-7a8b-4c9d-0e1f-2a3b4c5d6e7f",
          "question": "Any other feedback?",
          "type": "text",
          "position": {
            "type": "end"
          }
        }
      ],
      "updated": [
        {
          "question_id": "q-a3f8d1b2-4e5c-4c2a-9d11-0f8b7e6a1c42",
          "changed_fields": [
            "question",
            "required"
          ]
        }
      ],
      "deleted": [],
      "reordered": false
    },
    "logic_rule_changes": {
      "added": 0,
      "updated": 0,
      "deleted": 0
    },
    "option_changes": {
      "added": [
        {
          "question_id": "q-7b2f4e11-9c3a-4a1d-8e22-5f9d3b0a71e8",
          "label": "New Feature"
        }
      ],
      "renamed": [
        {
          "question_id": "q-a3f8d1b2-4e5c-4c2a-9d11-0f8b7e6a1c42",
          "option_id": "opt_a1b2c3d4",
          "label": "Main Dashboard"
        }
      ],
      "deleted": [],
      "reordered": []
    },
    "metadata_changes": {
      "title": "Customer Satisfaction Q1 — Updated"
    }
  }
}

delete/surveys/{survey_id}

Delete survey

Permanently delete a survey. Owner only.

Parameters
NameInTypeRequiredDescription
survey_idpathstring (uuid)YesSurvey ID to delete.
Idempotency-KeyheaderstringYesRequired idempotency key for write operations. Reuse the same value when retrying the same request.
Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

  • deleted truerequired

Example response

{
  "deleted": true
}

post/surveys/{survey_id}/publish

Publish survey

Publish the existing survey working draft to live. If already published, updates live survey from the current working draft. Does not create new surveys or accept inline content edits. Returns a public_url — always show this link to the user after publishing.

Parameters
NameInTypeRequiredDescription
survey_idpathstring (uuid)YesExisting survey ID to publish
Idempotency-KeyheaderstringYesRequired idempotency key for write operations. Reuse the same value when retrying the same request.
Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

  • id string (uuid)

    Survey ID (UUID)

  • is_published boolean
  • public_url stringrequired

    Public survey URL. Always show this link to the user after publishing.

Example response

{
  "id": "9f3a8b12-4c5d-4e6f-8a1b-0c2d3e4f5a6b",
  "is_published": true,
  "public_url": "https://www.empirio.ai/s/9f3a8b12-4c5d-4e6f-8a1b-0c2d3e4f5a6b"
}

post/surveys/{survey_id}/unpublish

Unpublish survey

Unpublish a survey and move it back to draft status (owner only).

Parameters
NameInTypeRequiredDescription
survey_idpathstring (uuid)YesSurvey ID to unpublish.
Idempotency-KeyheaderstringYesRequired idempotency key for write operations. Reuse the same value when retrying the same request.
Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

  • id string (uuid)

    Survey ID (UUID)

  • is_published false

Example response

{
  "id": "9f3a8b12-4c5d-4e6f-8a1b-0c2d3e4f5a6b",
  "is_published": false
}

post/surveys/{survey_id}/duplicate

Duplicate survey

Create a copy of an existing survey (unpublished draft).

Parameters
NameInTypeRequiredDescription
survey_idpathstring (uuid)YesSurvey ID to duplicate
Idempotency-KeyheaderstringYesRequired idempotency key for write operations. Reuse the same value when retrying the same request.
Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

  • id string (uuid)

    Survey ID (UUID)

  • title string

Example response

{
  "id": "d4e5f6a7-8b9c-4d0e-9f1a-2b3c4d5e6f70",
  "title": "Customer Satisfaction Q1 (Copy)"
}

Responses

List, aggregate, export, cross-tabulate, and delete survey responses.

get/surveys/{survey_id}/responses

List responses

List survey participations with global row ranking (row_no) for pagination/deletion workflows. Returns full answers and response metadata without internal IDs.

Parameters
NameInTypeRequiredDescription
survey_idpathstring (uuid)YesSurvey ID.
date_fromquerystringNoISO 8601 date filter start
date_toquerystringNoISO 8601 date filter end
include_incomplete_participationsquerybooleanNoInclude incomplete participations with meaningful saved progress
limitqueryintegerNoDefault 100
offsetqueryintegerNoZero-based pagination offset.
Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

  • responses array of objectrequired
    • row_no integer (nullable)required

      Global row ranking (1-based). Stable within a survey for pagination.

    • answers map of objectrequired

      Flat record keyed by `question_id`. Answer values are resolved to labels (not internal option IDs). Matrix answers are `{rowLabel: columnLabel}`. Free-text 'other' answers appear under `<question_id>_other` keys.

    • created_at string (nullable)required
    • completed_at string (nullable)required
    • duration_seconds number (nullable)required
    • ended_by_logic booleanrequired
    • locale string (nullable)required
    • participation_type "response" | "incomplete"required
  • total_count integerrequired
  • has_more booleanrequired

Example response

{
  "responses": [
    {
      "row_no": 1,
      "answers": {
        "q-a3f8d1b2-4e5c-4c2a-9d11-0f8b7e6a1c42": "Dashboard",
        "q-7b2f4e11-9c3a-4a1d-8e22-5f9d3b0a71e8": [
          "Pricing",
          "Support"
        ],
        "q-3f8d4b21-7a95-4c6e-89b1-2d5e7f1c9a34": 5,
        "q-6e2c9a74-5b38-4f1d-92e7-8a4c7b3d1f05": 9
      },
      "created_at": "2026-04-05T14:22:31.000Z",
      "completed_at": "2026-04-05T14:25:38.000Z",
      "duration_seconds": 187,
      "ended_by_logic": false,
      "locale": "de-DE",
      "participation_type": "response"
    },
    {
      "row_no": 2,
      "answers": {
        "q-a3f8d1b2-4e5c-4c2a-9d11-0f8b7e6a1c42": "API",
        "q-a3f8d1b2-4e5c-4c2a-9d11-0f8b7e6a1c42_other": null,
        "q-7b2f4e11-9c3a-4a1d-8e22-5f9d3b0a71e8": [
          "Speed"
        ],
        "q-3f8d4b21-7a95-4c6e-89b1-2d5e7f1c9a34": 4,
        "q-6e2c9a74-5b38-4f1d-92e7-8a4c7b3d1f05": 7
      },
      "created_at": "2026-04-05T15:10:02.000Z",
      "completed_at": "2026-04-05T15:13:45.000Z",
      "duration_seconds": 223,
      "ended_by_logic": false,
      "locale": "en-US",
      "participation_type": "response"
    }
  ],
  "total_count": 142,
  "has_more": true
}

delete/surveys/{survey_id}/responses

Delete responses

Delete responses by ranked row numbers (from responses_list), or delete all responses for a survey. Owner only. Matching responses are permanently soft-deleted.

Parameters
NameInTypeRequiredDescription
survey_idpathstring (uuid)YesSurvey ID.
Idempotency-KeyheaderstringYesRequired idempotency key for write operations. Reuse the same value when retrying the same request.
Request body

Required

  • mode "rows" | "all"required

    Delete only selected ranked rows ('rows') or delete all responses ('all').

  • row_numbers array of integer

    Global response row numbers from responses_list (1-based) when mode='rows'. Max 1000 per request.

  • date_from string

    Date filter used for rank resolution (must match responses_list)

  • date_to string

    Date filter used for rank resolution (must match responses_list)

  • include_incomplete_participations boolean

    Incomplete participation filter used for rank resolution (must match responses_list)

Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

  • deleted_count integerrequired

    Number of responses that were soft-deleted.

  • unresolved_row_numbers array of integerrequired

    Row numbers from the request that could not be resolved to an existing response.

Example response

{
  "deleted_count": 3,
  "unresolved_row_numbers": []
}

get/surveys/{survey_id}/responses/aggregates

Get response aggregates

Get aggregate statistics (counts per answer) for survey questions.

Parameters
NameInTypeRequiredDescription
survey_idpathstring (uuid)YesSurvey ID.
question_idsqueryarray of stringNoSpecific question IDs (omit for all, max 200)
Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

  • aggregates objectrequired

    Aggregate statistics produced by the analytics core. Field names use camelCase (`questionId`, `totalAnswered`, etc.) because this payload mirrors the analytics core's internal contract — the `questionId` value is the same opaque identifier other endpoints expose as `question_id`, so you can still use it as a join key.

    • totalFiltered integerrequired

      Total number of responses after filters, before per-question answer-counting.

    • questions array of objectrequired
      • questionId stringrequired

        Stable question identifier. Same value as `question_id` emitted by `survey_get` and webhooks — camelCased here because this payload comes directly from the analytics core, which uses camelCase for its internal contract.

      • questionText stringrequired
      • questionType stringrequired
      • totalAnswered integerrequired
      • skipped integerrequired
      • buckets array of objectrequired
        • value objectrequired

          Display label or numeric value for this bucket (option IDs are resolved to labels).

        • count integerrequired
        • percentage numberrequired

          Share of total answered responses for this bucket (one decimal, e.g. 42.5).

Example response

{
  "aggregates": {
    "totalFiltered": 142,
    "questions": [
      {
        "questionId": "q-a3f8d1b2-4e5c-4c2a-9d11-0f8b7e6a1c42",
        "questionText": "Which product area do you use most?",
        "questionType": "multiple-choice",
        "totalAnswered": 142,
        "skipped": 0,
        "buckets": [
          {
            "value": "Dashboard",
            "count": 78,
            "percentage": 54.9
          },
          {
            "value": "Reports",
            "count": 41,
            "percentage": 28.9
          },
          {
            "value": "API",
            "count": 23,
            "percentage": 16.2
          }
        ]
      },
      {
        "questionId": "q-3f8d4b21-7a95-4c6e-89b1-2d5e7f1c9a34",
        "questionText": "How would you rate our service?",
        "questionType": "rating",
        "totalAnswered": 142,
        "skipped": 0,
        "buckets": [
          {
            "value": 5,
            "count": 71,
            "percentage": 50
          },
          {
            "value": 4,
            "count": 48,
            "percentage": 33.8
          },
          {
            "value": 3,
            "count": 15,
            "percentage": 10.6
          },
          {
            "value": 2,
            "count": 6,
            "percentage": 4.2
          },
          {
            "value": 1,
            "count": 2,
            "percentage": 1.4
          }
        ]
      }
    ]
  }
}

get/surveys/{survey_id}/responses/crosstab

Cross-tabulate responses

Cross-tabulate answers between two survey questions.

Parameters
NameInTypeRequiredDescription
survey_idpathstring (uuid)YesSurvey ID.
question_xquerystringYesFirst question ID (rows)
question_yquerystringYesSecond question ID (columns)
Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

  • crosstab objectrequired

    Cross-tabulation between the two requested questions. Field names are camelCase because this payload mirrors the analytics core's internal contract; row/column labels are already label-resolved.

    • rowQuestion objectrequired

      Row question definition. `id` is the same stable question identifier as `question_id` elsewhere.

      • id stringrequired

        Stable opaque question identifier. Same value emitted by webhooks and returned by survey_get — safe to use as a join key across REST and webhook consumers.

      • text stringrequired
    • colQuestion objectrequired

      Column question definition.

      • id stringrequired

        Stable opaque question identifier. Same value emitted by webhooks and returned by survey_get — safe to use as a join key across REST and webhook consumers.

      • text stringrequired
    • matrix array of objectrequired
      • rowValue stringrequired

        Row label.

      • rowTotal integerrequired
      • columns array of objectrequired
        • colValue stringrequired

          Column label for this cell.

        • count integerrequired
        • rowPercentage numberrequired

          Row-normalised percentage (one decimal, e.g. 66.7).

    • truncated booleanrequired

      True when the row/column set was truncated to stay under the max-categories limit.

Example response

{
  "crosstab": {
    "rowQuestion": {
      "id": "q-a3f8d1b2-4e5c-4c2a-9d11-0f8b7e6a1c42",
      "text": "Which product area do you use most?"
    },
    "colQuestion": {
      "id": "q-3f8d4b21-7a95-4c6e-89b1-2d5e7f1c9a34",
      "text": "How would you rate our service?"
    },
    "matrix": [
      {
        "rowValue": "Dashboard",
        "rowTotal": 78,
        "columns": [
          {
            "colValue": "1",
            "count": 0,
            "rowPercentage": 0
          },
          {
            "colValue": "2",
            "count": 2,
            "rowPercentage": 2.6
          },
          {
            "colValue": "3",
            "count": 3,
            "rowPercentage": 3.8
          },
          {
            "colValue": "4",
            "count": 18,
            "rowPercentage": 23.1
          },
          {
            "colValue": "5",
            "count": 55,
            "rowPercentage": 70.5
          }
        ]
      },
      {
        "rowValue": "Reports",
        "rowTotal": 41,
        "columns": [
          {
            "colValue": "1",
            "count": 1,
            "rowPercentage": 2.4
          },
          {
            "colValue": "2",
            "count": 2,
            "rowPercentage": 4.9
          },
          {
            "colValue": "3",
            "count": 6,
            "rowPercentage": 14.6
          },
          {
            "colValue": "4",
            "count": 15,
            "rowPercentage": 36.6
          },
          {
            "colValue": "5",
            "count": 17,
            "rowPercentage": 41.5
          }
        ]
      },
      {
        "rowValue": "API",
        "rowTotal": 23,
        "columns": [
          {
            "colValue": "1",
            "count": 1,
            "rowPercentage": 4.3
          },
          {
            "colValue": "2",
            "count": 2,
            "rowPercentage": 8.7
          },
          {
            "colValue": "3",
            "count": 6,
            "rowPercentage": 26.1
          },
          {
            "colValue": "4",
            "count": 6,
            "rowPercentage": 26.1
          },
          {
            "colValue": "5",
            "count": 8,
            "rowPercentage": 34.8
          }
        ]
      }
    ],
    "truncated": false
  }
}

get/surveys/{survey_id}/responses/questions

Get question definitions

Get the question definitions for a survey.

Parameters
NameInTypeRequiredDescription
survey_idpathstring (uuid)YesSurvey ID.
Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

  • questions array of objectrequired
    • question_id string (nullable)required

      Stable question identifier. Identical to the `question_id` returned by `survey_get` and emitted by webhooks, so the three surfaces share one join key.

    • title string (nullable)required
    • type string (nullable)required
    • is_required booleanrequired
    • has_open_text booleanrequired
    • is_draft_only boolean

      Present and true only for questions that exist in the working draft but not the published survey.

  • total_questions integerrequired

Example response

{
  "questions": [
    {
      "question_id": "q-a3f8d1b2-4e5c-4c2a-9d11-0f8b7e6a1c42",
      "title": "Which product area do you use most?",
      "type": "multiple-choice",
      "is_required": true,
      "has_open_text": true
    },
    {
      "question_id": "q-7b2f4e11-9c3a-4a1d-8e22-5f9d3b0a71e8",
      "title": "Which features matter most to you?",
      "type": "checkbox",
      "is_required": false,
      "has_open_text": false
    },
    {
      "question_id": "q-3f8d4b21-7a95-4c6e-89b1-2d5e7f1c9a34",
      "title": "How would you rate our service?",
      "type": "rating",
      "is_required": true,
      "has_open_text": false
    },
    {
      "question_id": "q-6e2c9a74-5b38-4f1d-92e7-8a4c7b3d1f05",
      "title": "How likely are you to recommend us?",
      "type": "nps",
      "is_required": true,
      "has_open_text": false
    }
  ],
  "total_questions": 4
}

get/surveys/{survey_id}/responses/stats

Get survey statistics

Get survey-level statistics (views, starts, completions, incompletes, and average duration).

Parameters
NameInTypeRequiredDescription
survey_idpathstring (uuid)YesSurvey ID.
Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

  • views integer
  • starts integer
  • completions integer
  • incompletes integer
  • average_duration_seconds number

Example response

{
  "views": 412,
  "starts": 198,
  "completions": 142,
  "incompletes": 56,
  "average_duration_seconds": 192
}

post/surveys/{survey_id}/responses/export/responses

Export responses

Export survey responses as CSV, XLSX, JSON, or SPSS. Returns a temporary signed download URL. By default only completed participations are exported unless include_incomplete_participations=true.

Parameters
NameInTypeRequiredDescription
survey_idpathstring (uuid)YesSurvey ID.
Idempotency-KeyheaderstringYesRequired idempotency key for write operations. Reuse the same value when retrying the same request.
Request body
  • format "csv" | "xlsx" | "json" | "spss"

    Export format (default: csv)

  • date_from string

    ISO 8601 date filter start

  • date_to string

    ISO 8601 date filter end

  • include_incomplete_participations boolean

    Include incomplete participations (default: false = completed only).

  • time_zone string

    IANA timezone for exported date/time fields (e.g. Europe/Berlin).

  • answer_filters array of object

    Answer filters (max 100) with the same semantics as the frontend results filters.

    • question_id stringrequired

      Question ID to filter by

    • answer objectrequired

      Answer value to match (scalar or array depending on question type)

Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

  • format "csv" | "xlsx" | "json" | "spss"required
  • download_url stringrequired

    Temporary signed download URL (valid for ~1 hour).

  • expires_at stringrequired

    ISO timestamp when the signed download URL expires.

Example response

{
  "format": "xlsx",
  "download_url": "https://storage.empirio.ai/exports/9f3a8b12.../responses-1712345678.xlsx?token=...",
  "expires_at": "2026-04-10T17:45:00.000Z"
}

post/surveys/{survey_id}/responses/export/charts

Export charts

Export survey aggregate charts in one of six formats: native editable PowerPoint (pptx), image-based PowerPoint (pptx_images), landscape A4 PDF (pdf), Word document (docx), ZIP of PNG charts (zip_images), or structured chart data (chart_json). For binary formats, returns a temporary signed download URL (valid for 1 hour). For chart_json, returns structured chart data inline. By default only completed participations are included unless include_incomplete_participations=true.

Parameters
NameInTypeRequiredDescription
survey_idpathstring (uuid)YesSurvey ID.
Idempotency-KeyheaderstringYesRequired idempotency key for write operations. Reuse the same value when retrying the same request.
Request body
  • format "pptx" | "pptx_images" | "pdf" | "docx" | "zip_images" | "chart_json"

    Chart export format (default: pptx). 'pptx' = native editable PowerPoint, 'pptx_images' = PowerPoint with raster slides, 'pdf' = landscape A4 PDF, 'docx' = Word document, 'zip_images' = ZIP archive of PNG charts, 'chart_json' = structured chart data.

  • date_from string

    ISO 8601 date filter start

  • date_to string

    ISO 8601 date filter end

  • include_incomplete_participations boolean

    Include incomplete participations in chart export (default: false = completed only).

  • answer_filters array of object

    Answer filters (max 100) with the same semantics as the frontend results filters.

    • question_id stringrequired

      Question ID to filter by

    • answer objectrequired

      Answer value to match (scalar or array depending on question type)

  • time_zone string

    Reserved parity field for timezone-aware chart export pipelines.

Responses
StatusDescription
200Successful response
400Validation error
401Unauthorized
403Forbidden
404Not found
409Conflict
429Rate limited
500Internal error

200 response schema

object | object

Example response

{
  "format": "pptx",
  "download_url": "https://storage.empirio.ai/exports/9f3a8b12.../charts-1712345678.pptx?token=...",
  "expires_at": "2026-04-10T17:45:00.000Z"
}

Webhooks

Overview

empirio.ai can push survey responses to your server as soon as they are submitted. Webhooks complement the REST API — you register endpoints once, and empirio delivers signed JSON payloads every time a respondent finishes a survey.

PropertyValue
DirectionOutbound (empirio → your server)
Triggersurvey.response.submitted
TransportHTTPS only (private IP ranges rejected)
SigningHMAC-SHA256 over the raw request body
Content typeapplication/json
Max endpoints per survey3
Retries3 attempts with exponential backoff (immediate → +1 min → +10 min)
Plan requirementPro or Enterprise

Webhook endpoints are managed in Settings → Integrations in the empirio app. The signing secret is returned only once on creation — store it immediately.

Signing & Verification

Request headers

Every delivery arrives with these headers:

Content-Type: application/json
X-Empirio-Signature: sha256=<hex>
X-Empirio-Survey-Id: <uuid>
X-Empirio-Delivery-Id: <uuid>
User-Agent: empirio-webhook/1.0
Verifying the signature

The signature is an HMAC-SHA256 of the raw request body using your endpoint's signing secret. Always verify before trusting the payload.

import crypto from "node:crypto";

function verifyEmpirioSignature(rawBody, headerValue, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody, "utf8")
    .digest("hex");
  const received = (headerValue || "").replace(/^sha256=/, "");
  return crypto.timingSafeEqual(
    Buffer.from(expected, "hex"),
    Buffer.from(received, "hex"),
  );
}

Payload Structure

Envelope
{
  "event": "survey.response.submitted",
  "survey_id": "…",
  "survey_title": "Customer Satisfaction Q1",
  "responded_at": "2026-04-05T14:22:31.000Z",
  "response": {
    "duration_seconds": 187,
    "response_locale": "de-DE",
    "ended_by_logic": false,
    "answers": [ /* cell entries — see below */ ]
  }
}
Answer entries

Each element of response.answers represents one "cell". The shape mirrors how empirio's CSV export lays out columns, so one response produces the same decomposition in both formats.

Every entry includes question_id, question_title, and question_type. The rest of the shape depends on what kind of question the entry belongs to — entries are distinguished by field presence, not an explicit discriminator:

Emitted forExtra fieldvalueIdentify by
text, text-long, email, phone, date, number, rating, scale, nps, thumbs, yes-no, privacy, multiple-choice, dropdown, image-choice, text-rating—string / number / boolean / nullno extra field present
checkbox, image-checkbox (one entry per defined option)option_labelboolean (true if selected)option_label present
matrix (one entry per defined row)row_labelstring / null (column label)row_label present
ranking (one entry per rank position 1..N)rank_positionstring / null (option label at this position)rank_position present
any question with allowOther: true (free-text companion)other_text (replaces value)—other_text present

Note: a multiple-choice, checkbox, or dropdown question with allowOther: true emits its normal entries plus one trailing companion entry in the same response — so a single multiple-choice question can contribute two entries, and a checkbox with N options contributes N+1.

A consumer can route entries with a single switch:

for (const entry of response.answers) {
  if ("other_text" in entry)       { /* free-text "other" answer */ }
  else if ("option_label" in entry) { /* checkbox one-hot */ }
  else if ("row_label" in entry)    { /* matrix row */ }
  else if ("rank_position" in entry){ /* ranking position */ }
  else                              { /* scalar / single-select */ }
}
Value conventions
  • No localization. Booleans are real JSON true / false (including yes-no and privacy); localize for presentation on your side.
  • question_id is stable and persistent. Each question is assigned a UUID-based ID (format q-<uuid>) at creation time and keeps it for the survey's lifetime. Inserting, removing, or reordering questions does not shift existing IDs. Use question_id as your join key for longitudinal or repeated-response analysis.
  • Unanswered cells are always emitted. Every defined matrix row, rank position, and checkbox option produces an entry — unanswered ones carry value: null (or value: false for checkbox options). The schema is stable across edits to the same survey.
  • Survey order. Entries follow the survey's question order. Within a question, checkbox / matrix / ranking entries follow the order of the corresponding options / matrixRows arrays; the other_text companion comes last.
  • Detecting "Other" on single-select questions. For multiple-choice / dropdown with allowOther: true, read the main entry and its paired companion together:
    • Companion other_text: null → the respondent picked a regular option; the main entry's value is that option's label.
    • Companion other_text: "<string>" → the respondent picked "Other"; the main entry's value carries the same free text (or the literal string "Other" for legacy records that stored only the Other marker without a free-text side channel).
  • Orphaned answers are dropped. If a stored answer refers to a question that no longer exists, it is skipped — you never see it.
  • No scale bounds. Rating/scale/nps/thumbs entries do not include min / max; read these from the survey definition via GET /surveys/{survey_id} if you need them.

Question Types

Concrete entry shapes for every question type — the groups and ordering mirror the REST API → Question Types section so you can jump back and forth between the create-payload shape and the webhook-receive shape for the same type.

Every example shows what one question of that type contributes to the response.answers array. Any multiple-choice, checkbox, or dropdown question with allowOther: true emits one additional other_text entry after its main entries — see the dedicated example at the bottom of this page.

question_id values shown in the examples are real q-<uuid> strings — that is the actual format your webhook will receive. They stay stable across edits, so you can use them as join keys.

Question typeEntries per responseValueDistinguishing field
text, text-long, email, phone, date1string or null—
number, rating, scale, nps, thumbs1number or null—
yes-no, privacy1boolean or null—
multiple-choice, dropdown, image-choice, text-rating1option label or null—
checkbox, image-checkboxN (one per defined option)booleanoption_label
matrixR (one per defined row)column label or nullrow_label
rankingN (one per rank position 1..N)option label at this rank or nullrank_position
…plus allowOther: true+1—other_text (replaces value)
content— (not emitted)——
Selection
Single choice — multiple-choice

One entry whose value is the picked option's label (or the free-text answer when the user picked "Other").

{
  "question_id": "q-a3f8d1b2-4e5c-4c2a-9d11-0f8b7e6a1c42",
  "question_title": "How did you hear about us?",
  "question_type": "multiple-choice",
  "value": "Social media"
}
Checkboxes — checkbox

N entries — one per defined option — each carrying the option's label and a boolean value indicating whether the respondent selected it.

[
  {
    "question_id": "q-7b2f4e11-9c3a-4a1d-8e22-5f9d3b0a71e8",
    "question_title": "Which features do you use?",
    "question_type": "checkbox",
    "option_label": "Dashboard",
    "value": true
  },
  {
    "question_id": "q-7b2f4e11-9c3a-4a1d-8e22-5f9d3b0a71e8",
    "question_title": "Which features do you use?",
    "question_type": "checkbox",
    "option_label": "Reports",
    "value": false
  },
  {
    "question_id": "q-7b2f4e11-9c3a-4a1d-8e22-5f9d3b0a71e8",
    "question_title": "Which features do you use?",
    "question_type": "checkbox",
    "option_label": "API",
    "value": true
  }
]
Dropdown — dropdown

One entry whose value is the picked option's label (or the free-text answer when the user picked "Other").

{
  "question_id": "q-d94b1a6f-2e77-4b8e-87c1-3d4f9a2e05bd",
  "question_title": "Select your country",
  "question_type": "dropdown",
  "value": "Germany"
}
Yes / No — yes-no

One entry with a boolean value (or null if unanswered).

{
  "question_id": "q-5e3f8c29-7a14-4d6b-91e0-2a7c4b8f3d91",
  "question_title": "Would you use this product again?",
  "question_type": "yes-no",
  "value": true
}
Rating
Star rating — rating

One entry with a numeric value from 1 to max (or null if unanswered).

{
  "question_id": "q-ac8d0f12-5b43-4f2a-8c7e-1e9f6b3a7d42",
  "question_title": "How would you rate our service?",
  "question_type": "rating",
  "value": 4
}
Thumbs — thumbs

One entry with a numeric value from 1 to max (or null if unanswered).

{
  "question_id": "q-3b9e2c47-8f15-4a3d-92c6-7d1f8e4b5a09",
  "question_title": "Did you enjoy this experience?",
  "question_type": "thumbs",
  "value": 4
}
Number rating — scale

One entry with a numeric value from 1 to max (or null if unanswered).

{
  "question_id": "q-6d4a8f13-2e51-4b7c-83f9-5a2c9d6e1f78",
  "question_title": "How satisfied are you?",
  "question_type": "scale",
  "value": 8
}
Text rating — text-rating

One entry whose value is the label of the picked column (or null if unanswered).

{
  "question_id": "q-9c2e5a81-6b73-4d1f-8a4e-3b7f2d9c4e15",
  "question_title": "How do you feel?",
  "question_type": "text-rating",
  "value": "Good"
}
NPS — nps

One entry with a numeric value from 0 to 10 (or null if unanswered).

{
  "question_id": "q-1f7b4e92-5c86-4a3d-91f2-6e4a8c7d9b31",
  "question_title": "How likely are you to recommend us?",
  "question_type": "nps",
  "value": 9
}
Text & Input
Short text — text

One entry with a string value (or null if unanswered).

{
  "question_id": "q-4a8e2f16-3d95-4b7c-82a1-9f5e6c3d1b74",
  "question_title": "What is your name?",
  "question_type": "text",
  "value": "Jane Doe"
}
Long text — text-long

One entry with a string value (or null if unanswered).

{
  "question_id": "q-8d3c1e74-9f42-4b5a-87e6-2a1f7b4c9d53",
  "question_title": "Please share additional feedback",
  "question_type": "text-long",
  "value": "Great product, very easy to use."
}
Email — email

One entry with a string value (or null if unanswered).

{
  "question_id": "q-2e9f6a38-4c71-4d8b-93a5-1b7e4f2c8a69",
  "question_title": "Your email address",
  "question_type": "email",
  "value": "jane@example.com"
}
Phone — phone

One entry with a string value (or null if unanswered).

{
  "question_id": "q-7c4b9e12-5a38-4f6d-81e9-3a2f8b7c4d61",
  "question_title": "Your contact number",
  "question_type": "phone",
  "value": "+49 30 1234567"
}
Number — number

One entry with a numeric value (or null if unanswered).

{
  "question_id": "q-0b3e8a57-6d21-4f9c-84b7-5e1a9c3d7f28",
  "question_title": "How many employees does your company have?",
  "question_type": "number",
  "value": 42
}
Date — date

One entry with an ISO-8601 date string as value (or null if unanswered).

{
  "question_id": "q-5a7f2c83-1e96-4b4d-87c3-9d4e6a1f8b52",
  "question_title": "When did you first use our product?",
  "question_type": "date",
  "value": "2024-03-15"
}
Advanced
Matrix — matrix

R entries — one per defined row. Each entry carries the row's label and the column label the respondent picked as value. Unanswered rows still produce an entry with value: null.

[
  {
    "question_id": "q-3f8d4b21-7a95-4c6e-89b1-2d5e7f1c9a34",
    "question_title": "Rate each feature",
    "question_type": "matrix",
    "row_label": "Ease of use",
    "value": "Good"
  },
  {
    "question_id": "q-3f8d4b21-7a95-4c6e-89b1-2d5e7f1c9a34",
    "question_title": "Rate each feature",
    "question_type": "matrix",
    "row_label": "Performance",
    "value": null
  }
]
Ranking — ranking

N entries — one per rank position 1..N, with the option placed at that rank as value.

[
  {
    "question_id": "q-6e2c9a74-5b38-4f1d-92e7-8a4c7b3d1f05",
    "question_title": "Rank by importance",
    "question_type": "ranking",
    "rank_position": 1,
    "value": "Speed"
  },
  {
    "question_id": "q-6e2c9a74-5b38-4f1d-92e7-8a4c7b3d1f05",
    "question_title": "Rank by importance",
    "question_type": "ranking",
    "rank_position": 2,
    "value": "Reliability"
  },
  {
    "question_id": "q-6e2c9a74-5b38-4f1d-92e7-8a4c7b3d1f05",
    "question_title": "Rank by importance",
    "question_type": "ranking",
    "rank_position": 3,
    "value": "Price"
  }
]
Content — content

Content blocks (headings, dividers, static text) are not emitted — they are display-only and never appear in response.answers.

Privacy Policy — privacy

One entry with a boolean value — true if the respondent consented, false if they did not.

{
  "question_id": "q-9d1e4a68-3c72-4b5f-86a9-5d7c2f8e1b43",
  "question_title": "Privacy Policy",
  "question_type": "privacy",
  "value": true
}
allowOther: true companion

Any multiple-choice, checkbox, or dropdown question that was created with allowOther: true emits one additional entry after its main entries. This companion uses the key other_text in place of value — a consumer can identify it unambiguously by checking "other_text" in entry. The content is the respondent's free-text answer, or null if they did not use the "other" field.

{
  "question_id": "q-a3f8d1b2-4e5c-4c2a-9d11-0f8b7e6a1c42",
  "question_title": "How did you hear about us?",
  "question_type": "multiple-choice",
  "other_text": "From a conference"
}

Delivery Guarantees

  • At-least-once delivery. On any failure (non-2xx response, connection error, timeout) empirio retries up to 3 times total with exponential backoff. Ensure your handler is idempotent — use X-Empirio-Delivery-Id to deduplicate.
  • 12-second delivery timeout. Return a 2xx status within 12 seconds; longer work should be queued asynchronously on your side.

MCP

Overview

The Model Context Protocol (MCP) server allows AI agents — ChatGPT, Claude, and any MCP-compatible host — to interact with your empirio.ai surveys and response data.

MCP is available on every plan, including Free — no paid plan is required. Access is gated only by OAuth authentication and per-tool scope. (The REST API, CLI, and Webhooks remain Pro/Enterprise features.)

Server URL

https://platform.empirio.ai/mcp

Authentication

OAuth 2.1 with PKCE. Clients auto-discover endpoints via .well-known metadata — no manual configuration needed.

PropertyValue
ProtocolMCP Streamable HTTP (stateless)
AuthOAuth 2.1 + PKCE (auto-discovered)
Scopessurveys, responses

Scopes

ScopeAccess
surveysCreate, read, edit, delete, publish, unpublish, duplicate surveys
responsesList, aggregate, export, delete responses; cross-tabulation; chart export

All tools are listed regardless of the current token scope. If a tool requires a scope the token doesn't have, the server responds with HTTP 403 plus WWW-Authenticate: Bearer ... error="insufficient_scope" scope="<required-scope>" so the client can re-authorize with additional permissions. The advisory JSON-RPC body also includes error.data.code = "insufficient_scope" with the required and current scopes.

Write tools require idempotency_key in their MCP input payload.

Rate limits

MetricLimit
Read tools60 / minute per user
Write tools20 / minute per user
survey_create10 / hour and 50 / day per account

Quick start

ChatGPT (Responses API)

{
  "model": "gpt-4o",
  "tools": [{
    "type": "mcp",
    "server_label": "empirio",
    "server_url": "https://platform.empirio.ai/mcp",
    "require_approval": "never"
  }],
  "input": "List my surveys"
}

Claude (Settings → Connectors)

Add as a remote MCP server:

  • URL: https://platform.empirio.ai/mcp
  • Auth: OAuth 2.1 (auto-discovered via .well-known)

Examples

Once connected, your AI agent can handle prompts like these. Each example shows the tool sequence the model will typically follow.

  1. "Show me the responses from my latest customer-satisfaction survey." → survey_list → responses_survey_stats → responses_aggregates

  2. "Create a 5-question NPS survey for our new product and publish it." → survey_create (mode: "ai") → survey_publish

  3. "Cross-tabulate 'How satisfied are you?' against 'Would you recommend us?' for survey X." → responses_questions → responses_crosstab

  4. "Export the responses from survey X as an editable PowerPoint deck." → responses_export_charts (format: "pptx")

  5. "Delete the last 10 incomplete responses from survey X." → responses_list (include_incomplete_participations: true) → responses_delete (mode: "rows")

Error codes

Tool responses use a consistent envelope. On failure the response is { "ok": false, "error": { "code": ..., "message": ... } }.

CodeMeaningTypical resolution
insufficient_scopeToken is missing the surveys or responses scopeRe-authorize the client requesting the additional scope
not_authorizedAccess token is missing, invalid, or expiredRefresh or re-issue the token
forbiddenCaller has no access to the target resourceVerify ownership or collaboration role
not_foundSurvey, response, or related resource does not existCheck the survey_id / filters
validation_errorInput does not match the tool's schemaInspect error.message (and error.details if present) for the offending field and retry
idempotency_requiredMutating call did not supply idempotency_keyProvide a fresh UUID per new mutation
idempotency_in_progressA call with the same idempotency_key is still runningWait briefly, then retry with the same key
rate_limitedPer-user rate limit exceededBack off and retry after the indicated window
edge_errorUpstream platform service returned a 5xxSafe to retry with the same idempotency key
internal_errorUnexpected server-side failureRetry; contact support if it persists

Support

  • Email: info@empirio.ai
  • Privacy policy: empirio.ai/privacy-policy
  • Terms of service: empirio.ai/terms-of-service

Tools

Survey
ToolDescriptionWriteDestructive
survey_listList all surveys owned by or shared with the authenticated user
survey_getGet full survey details including questions, settings, and design
survey_createCreate a new survey draft✓
survey_editEdit the working draft✓
survey_publishPublish an existing draft or update a live survey from the working draft✓
survey_duplicateClone a survey as a new unpublished draft✓
survey_deletePermanently delete a survey and all its data✓✓
survey_unpublishUnpublish a live survey and move it back to draft status✓

All survey tools require the surveys scope.

Response
ToolDescriptionWriteDestructive
responses_listList paginated responses with optional date and answer filters
responses_aggregatesPer-question aggregate statistics (counts per answer)
responses_crosstabCross-tabulate answers between two survey questions
responses_questionsGet question definitions for a survey
responses_survey_statsSurvey statistics — views, starts, completions, incompletes, avg. duration
responses_deleteDelete responses by ranked row numbers or delete all✓✓
responses_exportExport responses as CSV, XLSX, JSON, or SPSS✓
responses_export_chartsExport charts as PowerPoint (editable or image-based), PDF, Word, ZIP of PNGs, or chart JSON✓

All response tools require the responses scope.

CLI

Overview

The empirio CLI (@empirio-ai/cli) is a command-line surface over the REST API. It ships as an npm package and delegates every call to the REST endpoints documented in this reference — so CLI and REST stay in parity by construction.

Package

npm install -g @empirio-ai/cli
# or
npx @empirio-ai/cli --help

Binary name: empirio. Requires Node.js ≥ 20. Source at github.com/empirio-de/empirio-ai/tree/main/cli.

See Commands (next in the sidebar) for the full reference of every empirio … subcommand, its parameters, and usage examples.

Authentication

Two modes. Precedence per invocation: EMPIRIO_API_KEY env var → OS keychain → ~/.config/empirio/credentials.json.

ModeCommandWhen
OAuth 2.1 (PKCE)empirio loginInteractive developer workstations
API keyEMPIRIO_API_KEY=sk_… empirio …CI / headless / automation

The OAuth flow opens the default browser, the user approves consent on www.empirio.ai, and the CLI receives the authorization code on an ephemeral http://127.0.0.1:<port>/callback listener (RFC 8252 §7.3). Tokens are stored in the OS keychain via keytar and fall back to a chmod 0600 file on systems where keytar is unavailable. Refresh tokens are rotated on every use; the access-token TTL is one hour.

Plan gate

All CLI operations require a Pro or Enterprise plan — with one exception: empirio whoami (GET /me) is exempt so that free-plan users can confirm their identity and current plan tier after login.

Global flags

FlagEffect
--jsonEmit machine-readable JSON instead of pretty tables
--quietSuppress non-error output
--idempotency-key <uuid>Override the auto-generated idempotency key (write operations only)

Object-valued body fields (metadata, design, settings, welcome_page, logic_rules, translations, …) accept a JSON string on the command line: empirio survey edit --metadata '{"title":"Renamed"}' ….

Environment overrides

For staging or local development:

EMPIRIO_PLATFORM_URL=https://platform.staging.empirio.ai \
empirio login

EMPIRIO_APP_URL is auto-derived from EMPIRIO_PLATFORM_URL (platform.* → app host) unless overridden explicitly.

Support

  • Email: info@empirio.ai
  • Privacy policy: empirio.ai/privacy-policy
  • Terms of service: empirio.ai/terms-of-service

Commands

This section documents every empirio … subcommand with its parameters and a usage example. Flags use kebab-case (e.g. --survey-id, --ai-prompt); object-valued flags accept a JSON string.

All examples assume you are authenticated (either via empirio login or EMPIRIO_API_KEY).


Authentication
empirio login

Start the browser-based OAuth 2.1 (PKCE) flow. On success, tokens are written to the OS keychain (or ~/.config/empirio/credentials.json fallback).

ParameterTypeRequiredDescription
--scopestringNoRequested OAuth scopes, space-separated. Default: surveys responses.
empirio login
# or request a narrower scope:
empirio login --scope "surveys"
empirio logout

Revoke the stored access + refresh tokens server-side and clear the local credential store.

empirio logout
empirio whoami

Return the authenticated user, plan, and active scopes. Exempt from the Pro/Enterprise plan gate so every authenticated caller can inspect their own state.

empirio whoami
empirio whoami --json

Survey
empirio survey list

List surveys owned by or shared with the authenticated user.

ParameterTypeRequiredDescription
--statusdraft | published | allNoFilter by publication status. Default: all.
--limitinteger (1–100)NoPage size. Default: 50.
--offsetinteger (≥ 0)NoZero-based pagination offset.
empirio survey list
empirio survey list --status published --limit 10
empirio survey list --json | jq '.surveys[].id'
empirio survey get

Fetch full survey details: metadata, questions (with stable question_id + option_id), settings, design, logic.

ParameterTypeRequiredDescription
--survey-idUUIDYesSurvey to retrieve.
empirio survey get --survey-id 9f3a8b12-4c5d-4e6f-8a1b-0c2d3e4f5a6b
empirio survey create

Create a new (unpublished) survey draft in either manual mode (you supply title + questions) or AI mode (you supply a prompt and the server generates the survey).

ParameterTypeRequiredDescription
--modemanual | aiYesCreation mode.
--titlestringIn manualSurvey title (manual mode only).
--ai-promptstringIn AIPrompt that describes the survey to generate (AI mode only).
--questionsJSON arrayNoPre-built question objects (manual mode). See Question Types in the REST sidebar for the per-type schema.
--metadataJSON objectNoSurvey-level metadata (title, description, display_mode, …).
--welcome-pageJSON objectNoWelcome page configuration.
--end-pageJSON objectNoEnd page configuration.
--designJSON objectNoDesign customization (template, primary color, …).
--settingsJSON objectNoSurvey behavior settings (including master_locale and auto_advance).
--idempotency-keyUUIDNoOverride the auto-generated idempotency key.

Manual mode:

empirio survey create \
  --mode manual \
  --title "Customer Satisfaction Q2" \
  --questions '[
    {"type":"nps","question":"How likely are you to recommend us?","required":true},
    {"type":"text-long","question":"What could we improve?"}
  ]' \
  --settings '{"master_locale":"en-US","auto_advance":true}'

AI mode:

empirio survey create \
  --mode ai \
  --ai-prompt "Five-question NPS survey for a mobile banking app, in German, with a content block explaining data-protection"

AI mode with design override:

empirio survey create \
  --mode ai \
  --ai-prompt "Short post-purchase CSAT survey" \
  --design '{"template":"modern","primary_color":"#ff5a5f"}' \
  --settings '{"master_locale":"de-DE","auto_advance":false}'

Rate limit: survey_create is additionally capped at 10 / hour and 50 / day per account. On overflow the call returns 429 with Retry-After.

empirio survey edit

Apply changes to the survey's working draft (does not publish). Manual mode accepts targeted section patches; AI mode regenerates or adjusts via prompt.

ParameterTypeRequiredDescription
--survey-idUUIDYesSurvey to edit.
--modemanual | aiYesEdit mode.
--ai-promptstringIn AIPrompt describing the change (AI mode only).
--question-operationsJSON arrayNoPer-question ops: add / update / delete / reorder.
--option-operationsJSON arrayNoPer-option ops inside a question.
--metadataJSON objectNoPatch survey title / description / display_mode.
--welcome-pageJSON objectNoReplace welcome-page config.
--end-pageJSON objectNoReplace end-page config.
--logic-rulesJSON objectNoConditional-logic rules (rule_id keyed).
--designJSON objectNoDesign override.
--settingsJSON objectNoSurvey behavior settings, including auto_advance for respondent auto-navigation.
--translationsJSON objectNoPer-locale translation patches.
--idempotency-keyUUIDNoOverride the auto-generated idempotency key.

Rename a question via manual mode:

empirio survey edit \
  --survey-id 9f3a8b12-4c5d-4e6f-8a1b-0c2d3e4f5a6b \
  --mode manual \
  --question-operations '[
    {"op":"update","question_id":"q-a3f8d1b2","changes":{"question":"Updated wording","required":true}}
  ]'

Add a new question at the end:

empirio survey edit --survey-id <uuid> --mode manual \
  --question-operations '[
    {"op":"add","position":{"type":"end"},"question":{"type":"text","question":"Any other feedback?"}}
  ]'

AI mode:

empirio survey edit --survey-id <uuid> --mode ai \
  --ai-prompt "Add a demographics section with age range and country"
empirio survey publish

Publish a draft, or update a live survey from its working draft. Owner-only.

ParameterTypeRequiredDescription
--survey-idUUIDYesSurvey to publish.
--idempotency-keyUUIDNoOverride the auto-generated idempotency key.
empirio survey publish --survey-id 9f3a8b12-4c5d-4e6f-8a1b-0c2d3e4f5a6b
empirio survey unpublish

Move a published survey back to draft state. Owner-only.

ParameterTypeRequiredDescription
--survey-idUUIDYesSurvey to unpublish.
empirio survey unpublish --survey-id <uuid>
empirio survey duplicate

Clone a survey as a new unpublished draft.

ParameterTypeRequiredDescription
--survey-idUUIDYesSource survey.
empirio survey duplicate --survey-id <uuid>
empirio survey delete

Permanently delete a survey and all its data. Owner-only. Destructive.

ParameterTypeRequiredDescription
--survey-idUUIDYesSurvey to delete.
--idempotency-keyUUIDNoOverride the auto-generated idempotency key.
empirio survey delete --survey-id <uuid>

Responses
empirio responses list

List individual responses for a survey with per-question answers and rank-based row_no.

ParameterTypeRequiredDescription
--survey-idUUIDYesSurvey whose responses to list.
--date-fromISO 8601NoFilter start.
--date-toISO 8601NoFilter end.
--include-incomplete-participationsbooleanNoInclude incomplete participations with saved progress. Default false.
--limitinteger (1–1000)NoDefault 100.
--offsetinteger (0–100000)NoZero-based offset.
empirio responses list --survey-id <uuid>
empirio responses list --survey-id <uuid> --date-from 2026-04-01 --limit 500
empirio responses aggregates

Get aggregate counts and percentages per question.

ParameterTypeRequiredDescription
--survey-idUUIDYesSurvey to aggregate.
--question-idscomma-separated listNoLimit to specific questions (max 200). Omit for all.
empirio responses aggregates --survey-id <uuid>
empirio responses aggregates --survey-id <uuid> --question-ids q-a3f8d1b2,q-7b2f4e11
empirio responses crosstab

Cross-tabulate two questions (rows × columns).

ParameterTypeRequiredDescription
--survey-idUUIDYesSurvey.
--question-xquestion_idYesRow question.
--question-yquestion_idYesColumn question.
empirio responses crosstab \
  --survey-id <uuid> \
  --question-x q-a3f8d1b2 \
  --question-y q-7b2f4e11
empirio responses questions

Return question definitions as used by the response endpoints (with question_id / option_id).

ParameterTypeRequiredDescription
--survey-idUUIDYesSurvey.
empirio responses questions --survey-id <uuid>
empirio responses survey-stats

Summary statistics: total responses, completion rate, average duration, last-response-at.

ParameterTypeRequiredDescription
--survey-idUUIDYesSurvey.
empirio responses survey-stats --survey-id <uuid>
empirio responses delete

Delete individual responses by rank (--mode rows with --row-numbers from responses list) or wipe all responses (--mode all). Destructive.

ParameterTypeRequiredDescription
--survey-idUUIDYesSurvey.
--moderows | allYesTargeting mode.
--row-numberscomma-separated integersIn rows modeRanks from responses list (1-based, max 1000).
--date-from / --date-toISO 8601NoMust mirror the responses list filter used for rank resolution.
--include-incomplete-participationsbooleanNoMust mirror the responses list filter.
--idempotency-keyUUIDNoOverride the auto-generated idempotency key.
# Delete responses at row 3, 7, 12 (from the last `responses list` you ran):
empirio responses delete --survey-id <uuid> --mode rows --row-numbers 3,7,12

# Wipe everything:
empirio responses delete --survey-id <uuid> --mode all
empirio responses export

Export responses as CSV, XLSX, JSON, or SPSS. Returns a temporary signed download URL.

ParameterTypeRequiredDescription
--survey-idUUIDYesSurvey.
--formatcsv | xlsx | json | spssNoDefault csv.
--date-from / --date-toISO 8601NoFilter window.
--include-incomplete-participationsbooleanNoDefault false.
--time-zoneIANA TZNoe.g. Europe/Berlin for exported timestamps.
--answer-filtersJSON arrayNoFilter by specific answer values (max 100).
--idempotency-keyUUIDNoOverride the auto-generated idempotency key.
empirio responses export --survey-id <uuid> --format xlsx
empirio responses export --survey-id <uuid> --format csv --time-zone Europe/Berlin
empirio responses export-charts

Export aggregate charts in one of six formats: native editable PowerPoint (pptx), image-based PowerPoint (pptx_images), landscape A4 PDF (pdf), Word document (docx), ZIP of PNG charts (zip_images), or structured chart JSON (chart_json).

ParameterTypeRequiredDescription
--survey-idUUIDYesSurvey.
--formatpptx | pptx_images | pdf | docx | zip_images | chart_jsonNoDefault pptx. Binary formats return a 1h signed download URL; chart_json returns inline content.
--date-from / --date-toISO 8601NoFilter window.
--include-incomplete-participationsbooleanNoDefault false.
--time-zoneIANA TZNoe.g. Europe/Berlin.
--answer-filtersJSON arrayNoSame as responses export.
--idempotency-keyUUIDNoOverride the auto-generated idempotency key.
empirio responses export-charts --survey-id <uuid> --format pptx
empirio responses export-charts --survey-id <uuid> --format pdf
empirio responses export-charts --survey-id <uuid> --format docx
empirio responses export-charts --survey-id <uuid> --format zip_images
empirio responses export-charts --survey-id <uuid> --format chart_json --json > charts.json