---
title: "Publish survey"
method: POST
path: "/surveys/{id}/publish"
tags: ["Surveys"]
---

# Publish survey

`POST /surveys/{id}/publish`

## Request

- Base URL: `https://api.pickfu.com/v1`
- URL: `https://api.pickfu.com/v1/surveys/{id}/publish`
- Auth: HTTP bearer

Publishes a draft survey and begins collecting responses from the target audience.

### Prerequisites
- Survey must be in `unpublished` (draft) status
- Account must have sufficient credits
- All required fields must be complete (at least one question with options)

After publishing, the survey status changes to `pending` or `in-progress` and credits are deducted from your account.

## Path parameters

- `id` string, required

## Response `200`

Default Response

- object
  - `success` boolean
  - `data` object: Full survey data including questions, options, and metadata
    - `id` string: Unique survey GUID
    - `name` string, nullable: Survey name/title
    - `country` string: Two-letter country code (e.g. US, GB)
    - `currency` string: ISO 4217 currency derived from the survey target country
    - `sampleSize` number: Number of respondents requested
    - `numResponses` number, nullable: Number of responses collected so far
    - `status` string: Survey status: unpublished, in-progress, completed, or archived (deleted is also possible here for soft-deleted surveys, which the list endpoint excludes). Same vocabulary as the list endpoint — a survey always has the identical status string on both.
    - `targeting` string[]: Array of targeting trait permalinks applied to this survey
    - `reporting` string[]: Array of reporting trait permalinks for demographic breakdowns
    - `reportingDefaultsApplied` string[]: Reporting trait permalinks that were auto-injected as free defaults (based on plan level and country), not explicitly requested by the caller
    - `tags` object[]: Tags associated with the survey
      - `id` string, required: Opaque tag identifier (tag_<32-hex>)
      - `name` string, required: Tag name
    - `projects` object[]: Projects the survey belongs to
      - `id` string, required: Project GUID
      - `name` string, required: Project name
    - `surveyUrl` string: Public URL to view the survey results
    - `surveyIntent` string: AI-interpreted intent of the survey
    - `externalId` string: Optional opaque correlation key for your own records. Omitted when unset. Case-sensitive and byte-exact. Many surveys may share the same id. We do not interpret this value.
    - `template` object, nullable: The template the survey was made from and the limits it must meet to publish, or null. Edits that break a limit are rejected.
      - `id` string, nullable: Template id (cmb_...), or null when the template can no longer be addressed
      - `name` string
      - `constraints` object
        - `questionTypes` string[], required
        - `optionTypes` string[], required
        - `optionCount` object, required
          - `min` integer, required
          - `max` integer, required
        - `sampleSizes` string[], required
        - `defaultSampleSize` string, required
        - `countries` string[], required
        - `defaultCountry` string, required
    - `questions` object[]: Questions in the survey with options, responses, and AI metadata
      - `id` string: Question ID (GUID)
      - `type` string: Question type (head_to_head, ranked, open_ended, click_test, emoji_rating, star_rating, five_second_test, screen_recording, single_select, multi_select, likert, van_westendorp)
      - `variant` string, nullable: Question variant for specialized display modes
      - `value` string: Question text
      - `translation` string, nullable: Translated question text
      - `likertScale` 'agreement5' | 'agreement7' | 'frequency' | 'satisfaction' | 'null', nullable: Likert scale preset, returned for likert questions whose variant matches one of the canonical presets. Null for non-likert questions or legacy likert questions with no recognized variant.
      - `options` object[], nullable: Answer options for this question
        - `id` string: Option GUID for round-trip updates
        - `label` string: Option identifier label (e.g. "Option A")
        - `value` union: Option content: text string, Cloudinary URL, or array of URLs (image-set)
          - string
          - string[]
        - `type` string: Option data type: text, image, video, product, or image-set
        - `translation` string, nullable: Translated option text
        - `product` object, nullable: Product data for product-type options (ASIN or custom mockups)
          - `asin` string, nullable: Amazon ASIN (null for custom mockups)
          - `title` string: Product title
          - `price` number: Product price
          - `salePrice` number, nullable: Discounted/sale price. null when the product is not on sale.
          - `currency` string, nullable: Currency code for the price
          - `rating` number: Star rating (0-5)
          - `reviews` number: Number of reviews
          - `reviewScore` number, nullable: Alias of rating for public API round-trip
          - `numRatings` number, nullable: Alias of reviews for public API round-trip
          - `image` string: Product image URL
        - `imageSetDirection` string: Image stack layout direction: horizontal or vertical (image-set options only)
        - `mockupType` string: Product mockup rendering style: serp or listing (product options only)
        - `mockup` object: The mockup template a text option shows: `template` is its lookup key and `values` its stored field values, images at their PickFu media CDN copies. `values` leaves out the display field, which is the option's `value`. On update, leaving `mockup` out or sending it back unchanged keeps the page, and `mockup: null` removes the question's template.
          - `template` string
          - `values` object
      - `context` object, nullable: Additional context provided with the question (e.g. a product description or image)
        - `type` string: Context type (text or media)
        - `value` string: Context content: the text, or for media the hosted media URL
        - `text` string: Caption shown with the image. Present only on a media context that has one; send it back with the media URL to keep both.
        - `translation` string, nullable: Translated context text
      - `results` object, nullable: Aggregated, page-faithful summary of the outcome for this question — the same percentages and ranking shown on the results page. Null for question types without a simple per-option aggregate (open_ended, click_test, likert, five_second_test, screen_recording, round_robin) or before any responses arrive. For ranked questions, `votes` is the final instant-runoff tally the percentage is computed from, and differs from the first-choice count shown beside each option on the page; the winner exceeds 50%.
        - `totalResponses` number: Number of valid responses the percentages are computed against
        - `winningOptionId` string, nullable: GUID of the winning option, or null on a tie / before a winner is decided
        - `averageScore` number: Average rating, returned for rating questions only (emoji_rating, star_rating)
        - `options` object[]: Per-option results, ordered by rank (winner first)
          - `id` string: Option GUID
          - `label` string, nullable: Option label (e.g. "A")
          - `votes` number: Vote count; the final instant-runoff tally for ranked questions
          - `percentage` number: round(votes / totalResponses * 100), matching the results page
          - `rank` number, nullable: Final placement (1 = winner)
          - `isWinner` boolean: Whether this option is the winner
      - `responses` object[]: Respondent answers for this question
        - `id` string: Response ID
        - `pinned` boolean: Whether the response is pinned
        - `explanation` string, nullable: Written explanation from the respondent
        - `translation` string, nullable: Translated explanation text
        - `chosenOption` string, nullable: Label of the option chosen by the respondent
        - `selectedOptions` string[], nullable: Labels of options selected by the respondent for multi-select questions
        - `score` number, nullable: Rating score (for emoji_rating, star_rating question types)
        - `clicks` object[], nullable: Click coordinates for click_test questions
          - `number` number: Click sequence number
          - `xCoord` number: X coordinate of the click
          - `yCoord` number: Y coordinate of the click
        - `ranking` object[], nullable: Respondent's ranking of options for ranked question types. Each entry pairs a rank (1 = top pick) with the option label.
          - `rank` number: Rank position (1 = respondent's top pick)
          - `option` string, nullable: Option label at this rank
        - `media` string, nullable: Media URL (for screen_recording question types)
        - `mediaTranscription` string, nullable: Transcription of the recorded media
        - `matchup` object, nullable: Head-to-head matchup pairing shown to the respondent
          - `option1` string: First option in the matchup
          - `option2` string: Second option in the matchup
        - `statements` object[], nullable: Per-statement responses for likert question types. Each entry pairs a statement with the numeric value the respondent picked on the configured scale. DEV-3149.
          - `id` string: GUID of the statement (the question option)
          - `statement` string, nullable: Statement text
          - `value` number: Numeric value the respondent picked on the Likert scale (1..N for the configured scale)
        - `prices` object, nullable: Van Westendorp price-sensitivity responses. Four numeric prices the respondent provided for the four standardized PSM questions.
          - `tooCheap` number: Price at which the product would be so cheap that quality is questionable
          - `bargain` number: Price at which the product would be a bargain
          - `expensive` number: Price at which the product starts to feel expensive but is still worth considering
          - `tooExpensive` number: Price at which the product would be too expensive to consider
        - `respondent` object[]: Demographic traits of the respondent
          - `trait` string: Trait category name
          - `value` string: Trait value
      - `ai` object: AI-generated metadata such as summaries and key themes for this question
  - `cost` object: Cost information for the survey
    - `amount` number: Survey cost in dollars
    - `balance` number: Credit balance in dollars
    - `currency` string: Currency code (USD)
    - `canPayWithBalance` boolean: Whether balance covers the cost
    - `quotedCreditsCost` number: Post-discount cost in credits, after free credits applied (1 credit = $2 USD)
    - `creditsCostSubtotal` number: Pre-discount cost in credits (1 credit = $2 USD)
    - `discounts` array[]: Credit-side discount line items, each entry is [label, amountInCredits]. Amounts are denominated in credits so they subtract directly from creditsCostSubtotal
      - string[]
    - `discountsMoney` array[]: Dollar-side discount line items, each entry is [label, formattedDollarAmount] (e.g. "$20.08")
      - string[]
    - `planRequired` string, nullable: Plan tier required for current survey configuration, or null if the current plan is sufficient
    - `priceLines` array[]: The survey builder's price summary, each entry is [label, formattedDollarAmount] (e.g. ["Head-to-Head: Text Length (+1.2x)", "$10.00"]). Each amount is that line's cost for the whole sample: base price, extra options, text or video length, targeting and reporting. A $0.00 line is included free. The lines come before discounts: they add up to `creditsCostSubtotal` in dollars, and `discountsMoney` takes that down to `amount`
      - string[]
  - `completionEstimate` object: Estimated completion-time bucket for the current audience
    - `bucket` string: Completion-time bucket: SUPER_EASY (<2h), EASY (1-2 days), MEDIUM (up to 2 weeks), HARD (may take longer), MAY_NOT_COMPLETE
    - `estimatedMinutes` number, nullable: Estimated completion time in minutes, or null when unavailable
    - `status` string: ready when an estimate exists, pending while computing

## Other responses

- `400`: Default Response
- `401`: Default Response
- `402`: Default Response
- `403`: Default Response
- `404`: Default Response

---

[API](https://skmtc.dev/pickfu/apis/pickfu-api.md) · [All operations](https://skmtc.dev/pickfu/apis/pickfu-api/llms.txt) · [OpenAPI document](https://skmtc.dev/pickfu/apis/pickfu-api/revisions/919fc58413ea?raw)
