{"openapi":"3.1.0","info":{"title":"PickFu API","version":"2.0.0","description":"The PickFu v2 API lets a partner draft, fund, publish and read a survey without the PickFu UI.\n\n## Authentication\n\nEvery request carries `Authorization: Bearer <api key>`. Keys start with `sk_live_` or `sk_test_` and are minted in Settings → API keys. A key belongs to exactly one team and is the unit of authorization. Keys must carry explicit scopes — `read`, `write` and/or `spend`; a key without scopes is rejected with `403 api_key_unscoped`.\n\nOptionally send `X-PickFu-Team: tm_…`; if it does not match the key's team the request fails with `403 team_mismatch`.\n\n## Errors\n\nNon-2xx responses share one envelope: `{ \"error\": { \"type\", \"code\", \"message\", \"param?\" }, \"requestId\" }`. `requestId` equals the `X-Request-Id` response header.\n\n## Rate limits\n\nLimits are enforced per team. Every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`; a `429` adds `Retry-After` (seconds).\n\n## Lists\n\nList endpoints return `{ \"data\": [...], \"hasMore\": boolean, \"nextCursor\": string | null }`. Pass `nextCursor` back as `cursor` to fetch the next page. There are no offsets or totals.\n\n## Surveys and money\n\nA survey is created as an `unpublished` draft and costs nothing until it is published. `GET /v2/surveys/{id}` (and the create/update responses) carry `cost` — the quote for publishing, computed by the same pricing as the PickFu app, including plan and country pricing — and `creditBalance`. The quote is a contract: publish charges the amount last quoted or answers `409 quote_changed`.\n\n`POST /v2/surveys/{id}/publish` spends credits first (oldest expiry first), then charges any shortfall to the team's default saved card on the survey order. With no saved card it answers `402 insufficient_credits` with the shortfall and a suggested pack — buy it with `POST /v2/orders` on Stripe-hosted Checkout, wait for the order to be `paid`, then publish again. Cards are managed on the Stripe Billing Portal (`POST /v2/billing-portal-sessions`). `200` from publish means paid, not live.\n\n## Results\n\nSurvey responses are cursor-paged at `GET /v2/surveys/{id}/responses`. Completed surveys can be downloaded through the existing synchronous CSV exporter at `GET /v2/surveys/{id}/exports/csv`; unusually large studies may time out.\n\n## Webhooks\n\nCreate team endpoints at `/v2/webhook-endpoints` for `survey.completed` and `order.paid`. The creation response returns the signing secret once. Every delivery is an envelope `{ id, type, createdAt, data: { object } }`. Verify `X-PickFu-Signature: t=<unix-seconds>,v1=<hex-hmac>` by computing HMAC-SHA256 over `<t>.<raw-body>` with that secret.\n\n## Idempotency\n\n`POST /v2/orders` and `POST /v2/surveys/{id}/publish` require an `Idempotency-Key` header (unique per attempt, up to 255 characters). Retrying with the same key and the same body replays the original 2xx response with `Idempotent-Replayed: true`; a different body is `409 idempotency_mismatch`; an attempt still running is `409 idempotency_in_flight` with `Retry-After`. Keys live for 24 hours per team. Non-2xx responses are not replayed, and a publish 402 can be retried after funding.\n\nOnce a credit-pack checkout attempt starts, `POST /v2/orders` retains its purchase identity and parameters for 24 hours, including after a failed response. Retry the identical request with the same key to resume that order and Checkout Session; use a new key for a different purchase.","contact":{"name":"PickFu Support","email":"support@pickfu.com","url":"https://www.pickfu.com"}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"sk_live_… or sk_test_…","description":"API key minted in Settings → API keys, sent as `Authorization: Bearer sk_live_…`."}},"schemas":{"Error":{"type":"object","description":"Error envelope returned by every non-2xx /v2 response.","required":["error","requestId"],"properties":{"error":{"type":"object","required":["type","code","message"],"additionalProperties":true,"properties":{"type":{"type":"string","enum":["authentication_error","permission_error","invalid_request_error","card_error","rate_limit_error","api_error"],"description":"Coarse error family to branch on."},"code":{"type":"string","description":"Stable machine-readable reason, e.g. `insufficient_scope`, `team_mismatch`, `rate_limit_exceeded`."},"message":{"type":"string","description":"Human-readable explanation of what happened and what to do."},"param":{"type":"string","description":"The request parameter or header the error refers to, when there is one."}}},"requestId":{"type":"string","description":"Server request id — identical to the `X-Request-Id` response header. Quote it when contacting support."}}}}},"paths":{"/me":{"get":{"operationId":"getMe","summary":"Who am I","tags":["Me"],"description":"Returns the regular key's team, creator and scopes. Requires the `read` scope. Partner keys cannot access this endpoint.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["object","team","user","apiKey"],"properties":{"object":{"type":"string","enum":["me"]},"team":{"type":["null","object"],"required":["object","id","name"],"properties":{"object":{"type":"string","enum":["team"]},"id":{"type":"string","pattern":"^tm_[0-9a-f]{32}$","description":"Team id (`tm_…`). Regular API keys act for one team."},"name":{"type":["null","string"]}}},"user":{"type":["null","object"],"required":["object","id","email","name"],"properties":{"object":{"type":"string","enum":["user"]},"id":{"type":"string","pattern":"^usr_[0-9a-f]{32}$","description":"Canonical PickFu user id."},"email":{"type":"string"},"name":{"type":["null","string"]}},"description":"The creator is informational and may be null. Membership changes never change the team that owns the key."},"apiKey":{"type":"object","required":["object","name","scopes","livemode"],"properties":{"object":{"type":"string","enum":["api_key"]},"name":{"type":"string","description":"Label given in Settings."},"scopes":{"type":"array","items":{"type":"string","enum":["read","write","spend"]},"description":"Scopes granted to this key: read, write, spend."},"livemode":{"type":"boolean","description":"`true` for `sk_live_` keys, `false` for `sk_test_`."}}}}}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/embed-sessions":{"post":{"operationId":"createEmbedSession","summary":"Create a partner Builder, Results or Listing session","tags":["Embed"],"description":"Requires a partner API key with fixed integration access (no permission scopes). The key identifies the owning team or personal user; externalId identifies the partner customer. Use surveyId to resume, or a stable initializationId to create a Builder draft. Listing sessions accept neither survey identifier and do not create a survey. Deliver the token to the hosted iframe through the SDK, never in a URL.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["mode","parentOrigin","externalId"],"properties":{"mode":{"type":"string","enum":["builder","results","listing"]},"parentOrigin":{"type":"string","format":"uri","maxLength":255},"surveyId":{"type":"string","pattern":"^srv_[0-9a-f]{32}$","description":"Canonical PickFu survey id (`srv_` plus 32 hexadecimal characters)."},"initializationId":{"type":"string","minLength":16,"maxLength":128},"externalId":{"type":"string","minLength":1,"maxLength":255,"description":"Optional opaque correlation key for your own records. Omit when unset. Case-sensitive and byte-exact — send the identical string every time. Many surveys may share the same id. We do not interpret this value."}}}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":false,"required":["id","mode","token","expiresAt"],"properties":{"id":{"type":"string"},"mode":{"type":"string","enum":["builder","results"]},"token":{"type":"string"},"expiresAt":{"type":"integer"}}},{"type":"object","additionalProperties":false,"required":["mode","token","expiresAt"],"properties":{"mode":{"type":"string","enum":["listing"]},"token":{"type":"string"},"expiresAt":{"type":"integer"}}}]}}}},"400":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/surveys":{"post":{"operationId":"createSurvey","summary":"Create a draft survey","tags":["Surveys"],"description":"Creates a survey in `unpublished` (draft) status and returns it with a cost quote and the team credit balance. Nothing is charged until `POST /v2/surveys/{id}/publish`. Drafts may be created repeatedly — no `Idempotency-Key` is needed. Requires the `write` scope.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["questions"],"additionalProperties":false,"properties":{"externalId":{"type":"string","minLength":1,"maxLength":255,"description":"Optional opaque correlation key for your own records. Omit when unset. Case-sensitive and byte-exact — send the identical string every time. Many surveys may share the same id. We do not interpret this value."},"name":{"type":"string","minLength":1,"maxLength":255,"description":"Survey name (1–255 characters)."},"country":{"type":"string","minLength":2,"maxLength":2,"description":"Two-letter respondent country code (e.g. US, GB). Defaults to US."},"sampleSize":{"type":"integer","minimum":1,"description":"Number of respondents. Standard surveys: 15, 30, 50, 75, 100, 200, 300, 500. Screen-recording surveys: 5–75. Defaults to 50."},"targeting":{"type":"array","items":{"type":"string"},"description":"Targeting trait permalinks from GET /v2/traits (replace-all on PATCH). Omit for a general audience."},"reporting":{"type":"array","items":{"type":"string"},"description":"Reporting-breakdown trait permalinks (replace-all on PATCH). Free defaults for your plan are added automatically."},"questions":{"type":"array","minItems":1,"maxItems":24,"items":{"type":"object","required":["type","prompt"],"additionalProperties":false,"properties":{"type":{"type":"string","enum":["head_to_head","ranked","open_ended","click_test","emoji_rating","star_rating","five_second_test","screen_recording","single_select","multi_select","likert","van_westendorp"],"description":"Question type. Every type except screen_recording also collects a written explanation from each respondent, so a separate open_ended \"why?\" question is redundant."},"prompt":{"type":"string","minLength":1,"maxLength":255,"description":"The question shown to respondents (1–255 characters)."},"context":{"type":"object","additionalProperties":false,"description":"Optional context shown with the question: `text`, or an image/video `mediaUrl`.","properties":{"text":{"type":"string"},"mediaUrl":{"type":"string"}}},"likertScale":{"type":"string","enum":["agreement5","agreement7","frequency","satisfaction"],"description":"Likert scale preset (likert questions only). Defaults to agreement5."},"options":{"type":"array","items":{"type":"object","additionalProperties":false,"properties":{"value":{"type":"string","maxLength":5000,"description":"Text option."},"mediaUrl":{"type":"string","description":"Image or video URL. Use `POST /v2/media` to host a file first, or pass any public URL — it is re-hosted on the PickFu CDN."},"asin":{"type":"string","pattern":"^[A-Z0-9]{10}$","maxLength":10,"description":"Amazon ASIN; product details are fetched for the survey country."},"imageSet":{"type":"array","minItems":1,"description":"Image stack: an array of image URLs, or exactly one product reference `{ source: \"amazon\", id: \"<ASIN>\" }` that expands to the product gallery.","items":{"anyOf":[{"type":"string"},{"type":"object","required":["source","id"],"additionalProperties":false,"properties":{"source":{"type":"string","enum":["amazon","walmart","google_play","apple"]},"id":{"type":"string","minLength":1}}}]}},"imageSetDirection":{"type":"string","enum":["vertical","horizontal","a-plus"],"description":"Layout of an image stack (image-set options only)."},"mockup":{"type":"object","required":["type"],"additionalProperties":false,"description":"Custom product mockup (no ASIN). `type` picks the rendering: `serp` (search result tile) or `listing` (product page).","properties":{"type":{"type":"string","enum":["serp","listing"]},"image":{"type":"string"},"title":{"type":"string"},"numRatings":{"type":"number"},"reviewScore":{"type":"number"},"rating":{"type":"number"},"reviews":{"type":"number"},"price":{"type":"number"},"currency":{"type":"string"}}}}},"description":"Answer options. Count rules per type: head_to_head 2; ranked / single_select / multi_select 3–8; click_test / five_second_test exactly 1 image; likert 2–8 statements; open_ended / ratings / screen_recording 0–1."}}},"description":"Questions in display order (1–24)."}}}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["object","id","name","status","country","currency","sampleSize","numResponses","targeting","reporting","questions","cost","creditBalance","createdAt","publishedAt","completedAt","resultsUrl"],"properties":{"object":{"type":"string","enum":["survey"]},"id":{"type":"string","pattern":"^srv_[0-9a-f]{32}$","description":"Canonical PickFu survey id (`srv_` plus 32 hexadecimal characters)."},"externalId":{"type":["null","string"]},"name":{"type":["null","string"]},"status":{"type":"string","enum":["unpublished","in-progress","completed","archived","deleted"],"description":"unpublished (draft) → in-progress (published, collecting) → completed. archived surveys are hidden from the default list."},"country":{"type":"string"},"currency":{"type":"string","description":"Currency respondents see, derived from `country`."},"sampleSize":{"type":"integer"},"numResponses":{"type":["null","integer"]},"targeting":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"publishedAt":{"type":["null","string"]},"completedAt":{"type":["null","string"]},"resultsUrl":{"type":"string","description":"PickFu results page for this survey (requires a PickFu login)."},"reporting":{"type":"array","items":{"type":"string"}},"reportingDefaultsApplied":{"type":"array","items":{"type":"string"},"description":"Reporting traits added for free by the plan, not requested explicitly."},"questions":{"type":"array","items":{"type":"object","required":["object","id","type","prompt","likertScale","context","options","results"],"properties":{"object":{"type":"string","enum":["question"]},"id":{"type":"string","pattern":"^(?:qst|srv)_[0-9a-f]{32}$","description":"Question id (pass back on PATCH to edit in place)."},"type":{"type":"string","enum":["head_to_head","ranked","open_ended","click_test","emoji_rating","star_rating","five_second_test","screen_recording","single_select","multi_select","likert","van_westendorp","round_robin"]},"prompt":{"type":"string"},"likertScale":{"type":["null","string"],"description":"Likert preset, or null for non-likert questions / custom scales."},"context":{"type":["null","object"],"properties":{"text":{"type":"string"},"mediaUrl":{"type":"string"}}},"options":{"type":"array","items":{"type":"object","required":["object","id","label"],"properties":{"object":{"type":"string","enum":["option"]},"id":{"type":"string","pattern":"^opt_[0-9a-f]{32}$","description":"Option id (pass back on PATCH to edit in place)."},"label":{"type":["null","string"],"description":"Display label assigned by PickFu (e.g. \"A\")."},"value":{"type":"string","description":"Text option content."},"mediaUrl":{"type":"string","description":"CDN URL of an image/video option."},"asin":{"type":"string"},"imageSet":{"type":"array","items":{"type":"string"}},"imageSetDirection":{"type":"string","enum":["vertical","horizontal","a-plus"]},"mockup":{"type":"object","required":["type"],"additionalProperties":false,"description":"Custom product mockup (no ASIN). `type` picks the rendering: `serp` (search result tile) or `listing` (product page).","properties":{"type":{"type":"string","enum":["serp","listing"]},"image":{"type":"string"},"title":{"type":"string"},"numRatings":{"type":"number"},"reviewScore":{"type":"number"},"rating":{"type":"number"},"reviews":{"type":"number"},"price":{"type":"number"},"currency":{"type":"string"}}},"product":{"type":"object","description":"Product details fetched for an `asin` option (read-only).","properties":{"asin":{"type":["null","string"]},"title":{"type":["null","string"]},"price":{"type":["null","number"]},"salePrice":{"type":["null","number"]},"currency":{"type":["null","string"]},"rating":{"type":["null","number"]},"reviews":{"type":["null","number"]},"image":{"type":["null","string"]}}}}}},"results":{"type":["null","object"],"additionalProperties":false,"required":["totalResponses","winningOptionId","options"],"description":"Persisted aggregates, including partial results while collecting. Null for unanswered or unsupported questions, or unavailable aggregates.","properties":{"totalResponses":{"type":"integer","description":"Eligible respondents, capped at the survey sample size when positive."},"winningOptionId":{"type":["null","string"],"pattern":"^opt_[0-9a-f]{32}$","description":"Persisted winner, matching this question’s options[].id. Null when undecided; rank never establishes a winner."},"averageScore":{"type":"number","description":"Star/emoji rating average. Only present for ratings, which have empty results.options and no winner."},"options":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["id","label","votes","score","percentage","rank","isWinner"],"properties":{"id":{"type":"string","pattern":"^opt_[0-9a-f]{32}$","description":"Public option ID matching this question’s options[].id."},"label":{"type":["null","string"]},"votes":{"type":"integer","description":"Option votes; final runoff votes for ranked questions; matchup wins for round-robin."},"score":{"type":["null","number"],"description":"Votes / totalResponses × 100, rounded to 1 decimal for head-to-head/single-select/multi-select or 2 for ranked final tallies. Round-robin: persisted score, falling back to wins / (wins + losses) × 100 rounded to 2 decimals; null with no matchups. Multi-select scores need not sum to 100."},"percentage":{"type":["null","integer"],"description":"Whole-number rounding of the unrounded votes / totalResponses × 100 ratio. Round-robin rounds its score instead; null when that score is null."},"rank":{"type":["null","integer"],"description":"Persisted rank when available; otherwise score descending, then label ascending (unscored options last)."},"isWinner":{"type":"boolean","description":"True only for the persisted winningOptionId."}}}}}}}}},"cost":{"type":["null","object"],"required":["object","amount","currency","credits","subtotalCredits","discounts","planRequired","quotedAt"],"properties":{"object":{"type":"string","enum":["quote"]},"amount":{"type":"number","description":"Total in USD after discounts and free plan credits."},"currency":{"type":"string","enum":["USD"]},"credits":{"type":"number","description":"Total in credits (1 credit = $2) after discounts."},"subtotalCredits":{"type":"number","description":"Pre-discount total in credits."},"discounts":{"type":"array","items":{"type":"object","required":["label","credits","amount"],"properties":{"label":{"type":"string"},"credits":{"type":"number"},"amount":{"type":"number"}}}},"planRequired":{"type":["null","string"],"description":"Plan tier this configuration requires (publish will fail until the owning team or personal user is on it), or null."},"quotedAt":{"type":"string","description":"When this quote was issued. Publish honours the most recent quote; if the price has moved since, publish answers 409 quote_changed."}},"description":"Cost quote for publishing this survey. null only when pricing is temporarily unavailable."},"creditBalance":{"type":["null","object"],"required":["object","credits","amount","currency","coversCost"],"properties":{"object":{"type":"string","enum":["credit_balance"]},"credits":{"type":"number","description":"Remaining credits."},"amount":{"type":"number","description":"Remaining balance in USD."},"currency":{"type":"string","enum":["USD"]},"coversCost":{"type":"boolean","description":"True when the balance covers `cost.credits`."}},"description":"The owning team or personal user’s current credit balance."}}}}}},"400":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"listSurveys","summary":"List surveys","tags":["Surveys"],"description":"Lists surveys owned by the key’s team, newest first, as a cursor page of summaries (no options, no cost — fetch a survey for those). Pass `nextCursor` back as `cursor` for the next page. Requires the `read` scope.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"in":"query","name":"limit","required":false,"description":"Page size, 1–100. Defaults to 20."},{"schema":{"type":"string","minLength":1},"in":"query","name":"cursor","required":false,"description":"Opaque cursor from a previous response's `nextCursor`. Omit for the first page."},{"schema":{"type":"string","minLength":1,"maxLength":255},"in":"query","name":"externalId","required":false,"description":"Optional opaque correlation key for your own records. Omit when unset. Case-sensitive and byte-exact — send the identical string every time. Many surveys may share the same id. We do not interpret this value."},{"schema":{"type":"array","items":{"type":"string","enum":["unpublished","in-progress","completed","archived"]}},"in":"query","name":"status","required":false,"description":"Only surveys in these statuses. Repeat the parameter for several (`?status=unpublished&status=in-progress`). Default: everything except archived."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["data","hasMore","nextCursor"],"properties":{"data":{"type":"array","items":{"type":"object","required":["object","id","name","status","country","currency","sampleSize","numResponses","targeting","questions","createdAt","publishedAt","completedAt","resultsUrl"],"properties":{"object":{"type":"string","enum":["survey"]},"id":{"type":"string","pattern":"^srv_[0-9a-f]{32}$","description":"Canonical PickFu survey id (`srv_` plus 32 hexadecimal characters)."},"externalId":{"type":["null","string"]},"name":{"type":["null","string"]},"status":{"type":"string","enum":["unpublished","in-progress","completed","archived","deleted"],"description":"unpublished (draft) → in-progress (published, collecting) → completed. archived surveys are hidden from the default list."},"country":{"type":"string"},"currency":{"type":"string","description":"Currency respondents see, derived from `country`."},"sampleSize":{"type":"integer"},"numResponses":{"type":["null","integer"]},"targeting":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"publishedAt":{"type":["null","string"]},"completedAt":{"type":["null","string"]},"resultsUrl":{"type":"string","description":"PickFu results page for this survey (requires a PickFu login)."},"questions":{"type":"array","items":{"type":"object","required":["object","id","type","prompt"],"properties":{"object":{"type":"string","enum":["question"]},"id":{"type":"string","pattern":"^(?:qst|srv)_[0-9a-f]{32}$"},"type":{"type":"string","enum":["head_to_head","ranked","open_ended","click_test","emoji_rating","star_rating","five_second_test","screen_recording","single_select","multi_select","likert","van_westendorp","round_robin"]},"prompt":{"type":"string"}}}}}}},"hasMore":{"type":"boolean","description":"True when another page exists after this one."},"nextCursor":{"type":["null","string"],"description":"Pass as `cursor` to fetch the next page. `null` on the last page."}}}}}},"400":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/surveys/{id}":{"get":{"operationId":"getSurvey","summary":"Get a survey","tags":["Surveys"],"description":"Returns a survey with its questions, options and question-level results, the current cost quote and the owning team or personal user’s credit balance. Reading results retains the existing quoting behavior. The quote is a contract: publish charges the quoted amount or answers `409 quote_changed`. Accepts a regular key with `read` scope or a partner key with fixed integration access.","parameters":[{"schema":{"type":"string","pattern":"^srv_[0-9a-f]{32}$"},"in":"path","name":"id","required":true,"description":"Canonical PickFu survey id (`srv_` plus 32 hexadecimal characters)."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["object","id","name","status","country","currency","sampleSize","numResponses","targeting","reporting","questions","cost","creditBalance","createdAt","publishedAt","completedAt","resultsUrl"],"properties":{"object":{"type":"string","enum":["survey"]},"id":{"type":"string","pattern":"^srv_[0-9a-f]{32}$","description":"Canonical PickFu survey id (`srv_` plus 32 hexadecimal characters)."},"externalId":{"type":["null","string"]},"name":{"type":["null","string"]},"status":{"type":"string","enum":["unpublished","in-progress","completed","archived","deleted"],"description":"unpublished (draft) → in-progress (published, collecting) → completed. archived surveys are hidden from the default list."},"country":{"type":"string"},"currency":{"type":"string","description":"Currency respondents see, derived from `country`."},"sampleSize":{"type":"integer"},"numResponses":{"type":["null","integer"]},"targeting":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"publishedAt":{"type":["null","string"]},"completedAt":{"type":["null","string"]},"resultsUrl":{"type":"string","description":"PickFu results page for this survey (requires a PickFu login)."},"reporting":{"type":"array","items":{"type":"string"}},"reportingDefaultsApplied":{"type":"array","items":{"type":"string"},"description":"Reporting traits added for free by the plan, not requested explicitly."},"questions":{"type":"array","items":{"type":"object","required":["object","id","type","prompt","likertScale","context","options","results"],"properties":{"object":{"type":"string","enum":["question"]},"id":{"type":"string","pattern":"^(?:qst|srv)_[0-9a-f]{32}$","description":"Question id (pass back on PATCH to edit in place)."},"type":{"type":"string","enum":["head_to_head","ranked","open_ended","click_test","emoji_rating","star_rating","five_second_test","screen_recording","single_select","multi_select","likert","van_westendorp","round_robin"]},"prompt":{"type":"string"},"likertScale":{"type":["null","string"],"description":"Likert preset, or null for non-likert questions / custom scales."},"context":{"type":["null","object"],"properties":{"text":{"type":"string"},"mediaUrl":{"type":"string"}}},"options":{"type":"array","items":{"type":"object","required":["object","id","label"],"properties":{"object":{"type":"string","enum":["option"]},"id":{"type":"string","pattern":"^opt_[0-9a-f]{32}$","description":"Option id (pass back on PATCH to edit in place)."},"label":{"type":["null","string"],"description":"Display label assigned by PickFu (e.g. \"A\")."},"value":{"type":"string","description":"Text option content."},"mediaUrl":{"type":"string","description":"CDN URL of an image/video option."},"asin":{"type":"string"},"imageSet":{"type":"array","items":{"type":"string"}},"imageSetDirection":{"type":"string","enum":["vertical","horizontal","a-plus"]},"mockup":{"type":"object","required":["type"],"additionalProperties":false,"description":"Custom product mockup (no ASIN). `type` picks the rendering: `serp` (search result tile) or `listing` (product page).","properties":{"type":{"type":"string","enum":["serp","listing"]},"image":{"type":"string"},"title":{"type":"string"},"numRatings":{"type":"number"},"reviewScore":{"type":"number"},"rating":{"type":"number"},"reviews":{"type":"number"},"price":{"type":"number"},"currency":{"type":"string"}}},"product":{"type":"object","description":"Product details fetched for an `asin` option (read-only).","properties":{"asin":{"type":["null","string"]},"title":{"type":["null","string"]},"price":{"type":["null","number"]},"salePrice":{"type":["null","number"]},"currency":{"type":["null","string"]},"rating":{"type":["null","number"]},"reviews":{"type":["null","number"]},"image":{"type":["null","string"]}}}}}},"results":{"type":["null","object"],"additionalProperties":false,"required":["totalResponses","winningOptionId","options"],"description":"Persisted aggregates, including partial results while collecting. Null for unanswered or unsupported questions, or unavailable aggregates.","properties":{"totalResponses":{"type":"integer","description":"Eligible respondents, capped at the survey sample size when positive."},"winningOptionId":{"type":["null","string"],"pattern":"^opt_[0-9a-f]{32}$","description":"Persisted winner, matching this question’s options[].id. Null when undecided; rank never establishes a winner."},"averageScore":{"type":"number","description":"Star/emoji rating average. Only present for ratings, which have empty results.options and no winner."},"options":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["id","label","votes","score","percentage","rank","isWinner"],"properties":{"id":{"type":"string","pattern":"^opt_[0-9a-f]{32}$","description":"Public option ID matching this question’s options[].id."},"label":{"type":["null","string"]},"votes":{"type":"integer","description":"Option votes; final runoff votes for ranked questions; matchup wins for round-robin."},"score":{"type":["null","number"],"description":"Votes / totalResponses × 100, rounded to 1 decimal for head-to-head/single-select/multi-select or 2 for ranked final tallies. Round-robin: persisted score, falling back to wins / (wins + losses) × 100 rounded to 2 decimals; null with no matchups. Multi-select scores need not sum to 100."},"percentage":{"type":["null","integer"],"description":"Whole-number rounding of the unrounded votes / totalResponses × 100 ratio. Round-robin rounds its score instead; null when that score is null."},"rank":{"type":["null","integer"],"description":"Persisted rank when available; otherwise score descending, then label ascending (unscored options last)."},"isWinner":{"type":"boolean","description":"True only for the persisted winningOptionId."}}}}}}}}},"cost":{"type":["null","object"],"required":["object","amount","currency","credits","subtotalCredits","discounts","planRequired","quotedAt"],"properties":{"object":{"type":"string","enum":["quote"]},"amount":{"type":"number","description":"Total in USD after discounts and free plan credits."},"currency":{"type":"string","enum":["USD"]},"credits":{"type":"number","description":"Total in credits (1 credit = $2) after discounts."},"subtotalCredits":{"type":"number","description":"Pre-discount total in credits."},"discounts":{"type":"array","items":{"type":"object","required":["label","credits","amount"],"properties":{"label":{"type":"string"},"credits":{"type":"number"},"amount":{"type":"number"}}}},"planRequired":{"type":["null","string"],"description":"Plan tier this configuration requires (publish will fail until the owning team or personal user is on it), or null."},"quotedAt":{"type":"string","description":"When this quote was issued. Publish honours the most recent quote; if the price has moved since, publish answers 409 quote_changed."}},"description":"Cost quote for publishing this survey. null only when pricing is temporarily unavailable."},"creditBalance":{"type":["null","object"],"required":["object","credits","amount","currency","coversCost"],"properties":{"object":{"type":"string","enum":["credit_balance"]},"credits":{"type":"number","description":"Remaining credits."},"amount":{"type":"number","description":"Remaining balance in USD."},"currency":{"type":"string","enum":["USD"]},"coversCost":{"type":"boolean","description":"True when the balance covers `cost.credits`."}},"description":"The owning team or personal user’s current credit balance."}}}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateSurvey","summary":"Update a draft survey","tags":["Surveys"],"description":"Updates a draft survey as a whole: each field sent replaces the current value (questions, targeting and reporting are replace-all); omitted fields are unchanged. Returns the survey with a fresh cost quote. Only `unpublished` surveys can be updated — a published survey answers `409 survey_not_editable`. Requires the `write` scope.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","minProperties":1,"additionalProperties":false,"properties":{"externalId":{"type":"string","minLength":1,"maxLength":255,"description":"Optional opaque correlation key for your own records. Omit when unset. Case-sensitive and byte-exact — send the identical string every time. Many surveys may share the same id. We do not interpret this value."},"name":{"type":"string","minLength":1,"maxLength":255,"description":"Survey name (1–255 characters)."},"country":{"type":"string","minLength":2,"maxLength":2,"description":"Two-letter respondent country code (e.g. US, GB). Defaults to US."},"sampleSize":{"type":"integer","minimum":1,"description":"Number of respondents. Standard surveys: 15, 30, 50, 75, 100, 200, 300, 500. Screen-recording surveys: 5–75. Defaults to 50."},"targeting":{"type":"array","items":{"type":"string"},"description":"Targeting trait permalinks from GET /v2/traits (replace-all on PATCH). Omit for a general audience."},"reporting":{"type":"array","items":{"type":"string"},"description":"Reporting-breakdown trait permalinks (replace-all on PATCH). Free defaults for your plan are added automatically."},"questions":{"type":"array","minItems":1,"maxItems":24,"items":{"type":"object","required":["type","prompt"],"additionalProperties":false,"properties":{"id":{"type":"string","pattern":"^(?:qst|srv)_[0-9a-f]{32}$","description":"Question id from a previous response. Include it to update the question in place; omit it to create a new question."},"type":{"type":"string","enum":["head_to_head","ranked","open_ended","click_test","emoji_rating","star_rating","five_second_test","screen_recording","single_select","multi_select","likert","van_westendorp"],"description":"Question type. Every type except screen_recording also collects a written explanation from each respondent, so a separate open_ended \"why?\" question is redundant."},"prompt":{"type":"string","minLength":1,"maxLength":255,"description":"The question shown to respondents (1–255 characters)."},"context":{"type":"object","additionalProperties":false,"description":"Optional context shown with the question: `text`, or an image/video `mediaUrl`.","properties":{"text":{"type":"string"},"mediaUrl":{"type":"string"}}},"likertScale":{"type":"string","enum":["agreement5","agreement7","frequency","satisfaction"],"description":"Likert scale preset (likert questions only). Defaults to agreement5."},"options":{"type":"array","items":{"type":"object","additionalProperties":false,"properties":{"id":{"type":"string","pattern":"^opt_[0-9a-f]{32}$","description":"Option id from a previous response. Include it to update the option in place; omit it to create a new option."},"value":{"type":"string","maxLength":5000,"description":"Text option."},"mediaUrl":{"type":"string","description":"Image or video URL. Use `POST /v2/media` to host a file first, or pass any public URL — it is re-hosted on the PickFu CDN."},"asin":{"type":"string","pattern":"^[A-Z0-9]{10}$","maxLength":10,"description":"Amazon ASIN; product details are fetched for the survey country."},"imageSet":{"type":"array","minItems":1,"description":"Image stack: an array of image URLs, or exactly one product reference `{ source: \"amazon\", id: \"<ASIN>\" }` that expands to the product gallery.","items":{"anyOf":[{"type":"string"},{"type":"object","required":["source","id"],"additionalProperties":false,"properties":{"source":{"type":"string","enum":["amazon","walmart","google_play","apple"]},"id":{"type":"string","minLength":1}}}]}},"imageSetDirection":{"type":"string","enum":["vertical","horizontal","a-plus"],"description":"Layout of an image stack (image-set options only)."},"mockup":{"type":"object","required":["type"],"additionalProperties":false,"description":"Custom product mockup (no ASIN). `type` picks the rendering: `serp` (search result tile) or `listing` (product page).","properties":{"type":{"type":"string","enum":["serp","listing"]},"image":{"type":"string"},"title":{"type":"string"},"numRatings":{"type":"number"},"reviewScore":{"type":"number"},"rating":{"type":"number"},"reviews":{"type":"number"},"price":{"type":"number"},"currency":{"type":"string"}}}}},"description":"Answer options (replace-all). See POST /v2/surveys for the per-type count rules."}}},"description":"Replaces all questions (1–24). Include a question's `id` to edit it in place."}}}}}},"parameters":[{"schema":{"type":"string","pattern":"^srv_[0-9a-f]{32}$"},"in":"path","name":"id","required":true,"description":"Canonical PickFu survey id (`srv_` plus 32 hexadecimal characters)."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["object","id","name","status","country","currency","sampleSize","numResponses","targeting","reporting","questions","cost","creditBalance","createdAt","publishedAt","completedAt","resultsUrl"],"properties":{"object":{"type":"string","enum":["survey"]},"id":{"type":"string","pattern":"^srv_[0-9a-f]{32}$","description":"Canonical PickFu survey id (`srv_` plus 32 hexadecimal characters)."},"externalId":{"type":["null","string"]},"name":{"type":["null","string"]},"status":{"type":"string","enum":["unpublished","in-progress","completed","archived","deleted"],"description":"unpublished (draft) → in-progress (published, collecting) → completed. archived surveys are hidden from the default list."},"country":{"type":"string"},"currency":{"type":"string","description":"Currency respondents see, derived from `country`."},"sampleSize":{"type":"integer"},"numResponses":{"type":["null","integer"]},"targeting":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"publishedAt":{"type":["null","string"]},"completedAt":{"type":["null","string"]},"resultsUrl":{"type":"string","description":"PickFu results page for this survey (requires a PickFu login)."},"reporting":{"type":"array","items":{"type":"string"}},"reportingDefaultsApplied":{"type":"array","items":{"type":"string"},"description":"Reporting traits added for free by the plan, not requested explicitly."},"questions":{"type":"array","items":{"type":"object","required":["object","id","type","prompt","likertScale","context","options","results"],"properties":{"object":{"type":"string","enum":["question"]},"id":{"type":"string","pattern":"^(?:qst|srv)_[0-9a-f]{32}$","description":"Question id (pass back on PATCH to edit in place)."},"type":{"type":"string","enum":["head_to_head","ranked","open_ended","click_test","emoji_rating","star_rating","five_second_test","screen_recording","single_select","multi_select","likert","van_westendorp","round_robin"]},"prompt":{"type":"string"},"likertScale":{"type":["null","string"],"description":"Likert preset, or null for non-likert questions / custom scales."},"context":{"type":["null","object"],"properties":{"text":{"type":"string"},"mediaUrl":{"type":"string"}}},"options":{"type":"array","items":{"type":"object","required":["object","id","label"],"properties":{"object":{"type":"string","enum":["option"]},"id":{"type":"string","pattern":"^opt_[0-9a-f]{32}$","description":"Option id (pass back on PATCH to edit in place)."},"label":{"type":["null","string"],"description":"Display label assigned by PickFu (e.g. \"A\")."},"value":{"type":"string","description":"Text option content."},"mediaUrl":{"type":"string","description":"CDN URL of an image/video option."},"asin":{"type":"string"},"imageSet":{"type":"array","items":{"type":"string"}},"imageSetDirection":{"type":"string","enum":["vertical","horizontal","a-plus"]},"mockup":{"type":"object","required":["type"],"additionalProperties":false,"description":"Custom product mockup (no ASIN). `type` picks the rendering: `serp` (search result tile) or `listing` (product page).","properties":{"type":{"type":"string","enum":["serp","listing"]},"image":{"type":"string"},"title":{"type":"string"},"numRatings":{"type":"number"},"reviewScore":{"type":"number"},"rating":{"type":"number"},"reviews":{"type":"number"},"price":{"type":"number"},"currency":{"type":"string"}}},"product":{"type":"object","description":"Product details fetched for an `asin` option (read-only).","properties":{"asin":{"type":["null","string"]},"title":{"type":["null","string"]},"price":{"type":["null","number"]},"salePrice":{"type":["null","number"]},"currency":{"type":["null","string"]},"rating":{"type":["null","number"]},"reviews":{"type":["null","number"]},"image":{"type":["null","string"]}}}}}},"results":{"type":["null","object"],"additionalProperties":false,"required":["totalResponses","winningOptionId","options"],"description":"Persisted aggregates, including partial results while collecting. Null for unanswered or unsupported questions, or unavailable aggregates.","properties":{"totalResponses":{"type":"integer","description":"Eligible respondents, capped at the survey sample size when positive."},"winningOptionId":{"type":["null","string"],"pattern":"^opt_[0-9a-f]{32}$","description":"Persisted winner, matching this question’s options[].id. Null when undecided; rank never establishes a winner."},"averageScore":{"type":"number","description":"Star/emoji rating average. Only present for ratings, which have empty results.options and no winner."},"options":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["id","label","votes","score","percentage","rank","isWinner"],"properties":{"id":{"type":"string","pattern":"^opt_[0-9a-f]{32}$","description":"Public option ID matching this question’s options[].id."},"label":{"type":["null","string"]},"votes":{"type":"integer","description":"Option votes; final runoff votes for ranked questions; matchup wins for round-robin."},"score":{"type":["null","number"],"description":"Votes / totalResponses × 100, rounded to 1 decimal for head-to-head/single-select/multi-select or 2 for ranked final tallies. Round-robin: persisted score, falling back to wins / (wins + losses) × 100 rounded to 2 decimals; null with no matchups. Multi-select scores need not sum to 100."},"percentage":{"type":["null","integer"],"description":"Whole-number rounding of the unrounded votes / totalResponses × 100 ratio. Round-robin rounds its score instead; null when that score is null."},"rank":{"type":["null","integer"],"description":"Persisted rank when available; otherwise score descending, then label ascending (unscored options last)."},"isWinner":{"type":"boolean","description":"True only for the persisted winningOptionId."}}}}}}}}},"cost":{"type":["null","object"],"required":["object","amount","currency","credits","subtotalCredits","discounts","planRequired","quotedAt"],"properties":{"object":{"type":"string","enum":["quote"]},"amount":{"type":"number","description":"Total in USD after discounts and free plan credits."},"currency":{"type":"string","enum":["USD"]},"credits":{"type":"number","description":"Total in credits (1 credit = $2) after discounts."},"subtotalCredits":{"type":"number","description":"Pre-discount total in credits."},"discounts":{"type":"array","items":{"type":"object","required":["label","credits","amount"],"properties":{"label":{"type":"string"},"credits":{"type":"number"},"amount":{"type":"number"}}}},"planRequired":{"type":["null","string"],"description":"Plan tier this configuration requires (publish will fail until the owning team or personal user is on it), or null."},"quotedAt":{"type":"string","description":"When this quote was issued. Publish honours the most recent quote; if the price has moved since, publish answers 409 quote_changed."}},"description":"Cost quote for publishing this survey. null only when pricing is temporarily unavailable."},"creditBalance":{"type":["null","object"],"required":["object","credits","amount","currency","coversCost"],"properties":{"object":{"type":"string","enum":["credit_balance"]},"credits":{"type":"number","description":"Remaining credits."},"amount":{"type":"number","description":"Remaining balance in USD."},"currency":{"type":"string","enum":["USD"]},"coversCost":{"type":"boolean","description":"True when the balance covers `cost.credits`."}},"description":"The owning team or personal user’s current credit balance."}}}}}},"400":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/surveys/{id}/stop":{"post":{"operationId":"stopSurvey","summary":"Stop a survey","tags":["Surveys"],"description":"Stops an `in-progress` survey early. Responses collected so far are kept and the survey moves to `completed`; this cannot be undone. Drafts and completed surveys answer `409 survey_state_conflict`. Requires the `write` scope.","parameters":[{"schema":{"type":"string","pattern":"^srv_[0-9a-f]{32}$"},"in":"path","name":"id","required":true,"description":"Canonical PickFu survey id (`srv_` plus 32 hexadecimal characters)."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["object","id","name","status","country","currency","sampleSize","numResponses","targeting","reporting","questions","cost","creditBalance","createdAt","publishedAt","completedAt","resultsUrl"],"properties":{"object":{"type":"string","enum":["survey"]},"id":{"type":"string","pattern":"^srv_[0-9a-f]{32}$","description":"Canonical PickFu survey id (`srv_` plus 32 hexadecimal characters)."},"externalId":{"type":["null","string"]},"name":{"type":["null","string"]},"status":{"type":"string","enum":["unpublished","in-progress","completed","archived","deleted"],"description":"unpublished (draft) → in-progress (published, collecting) → completed. archived surveys are hidden from the default list."},"country":{"type":"string"},"currency":{"type":"string","description":"Currency respondents see, derived from `country`."},"sampleSize":{"type":"integer"},"numResponses":{"type":["null","integer"]},"targeting":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"publishedAt":{"type":["null","string"]},"completedAt":{"type":["null","string"]},"resultsUrl":{"type":"string","description":"PickFu results page for this survey (requires a PickFu login)."},"reporting":{"type":"array","items":{"type":"string"}},"reportingDefaultsApplied":{"type":"array","items":{"type":"string"},"description":"Reporting traits added for free by the plan, not requested explicitly."},"questions":{"type":"array","items":{"type":"object","required":["object","id","type","prompt","likertScale","context","options","results"],"properties":{"object":{"type":"string","enum":["question"]},"id":{"type":"string","pattern":"^(?:qst|srv)_[0-9a-f]{32}$","description":"Question id (pass back on PATCH to edit in place)."},"type":{"type":"string","enum":["head_to_head","ranked","open_ended","click_test","emoji_rating","star_rating","five_second_test","screen_recording","single_select","multi_select","likert","van_westendorp","round_robin"]},"prompt":{"type":"string"},"likertScale":{"type":["null","string"],"description":"Likert preset, or null for non-likert questions / custom scales."},"context":{"type":["null","object"],"properties":{"text":{"type":"string"},"mediaUrl":{"type":"string"}}},"options":{"type":"array","items":{"type":"object","required":["object","id","label"],"properties":{"object":{"type":"string","enum":["option"]},"id":{"type":"string","pattern":"^opt_[0-9a-f]{32}$","description":"Option id (pass back on PATCH to edit in place)."},"label":{"type":["null","string"],"description":"Display label assigned by PickFu (e.g. \"A\")."},"value":{"type":"string","description":"Text option content."},"mediaUrl":{"type":"string","description":"CDN URL of an image/video option."},"asin":{"type":"string"},"imageSet":{"type":"array","items":{"type":"string"}},"imageSetDirection":{"type":"string","enum":["vertical","horizontal","a-plus"]},"mockup":{"type":"object","required":["type"],"additionalProperties":false,"description":"Custom product mockup (no ASIN). `type` picks the rendering: `serp` (search result tile) or `listing` (product page).","properties":{"type":{"type":"string","enum":["serp","listing"]},"image":{"type":"string"},"title":{"type":"string"},"numRatings":{"type":"number"},"reviewScore":{"type":"number"},"rating":{"type":"number"},"reviews":{"type":"number"},"price":{"type":"number"},"currency":{"type":"string"}}},"product":{"type":"object","description":"Product details fetched for an `asin` option (read-only).","properties":{"asin":{"type":["null","string"]},"title":{"type":["null","string"]},"price":{"type":["null","number"]},"salePrice":{"type":["null","number"]},"currency":{"type":["null","string"]},"rating":{"type":["null","number"]},"reviews":{"type":["null","number"]},"image":{"type":["null","string"]}}}}}},"results":{"type":["null","object"],"additionalProperties":false,"required":["totalResponses","winningOptionId","options"],"description":"Persisted aggregates, including partial results while collecting. Null for unanswered or unsupported questions, or unavailable aggregates.","properties":{"totalResponses":{"type":"integer","description":"Eligible respondents, capped at the survey sample size when positive."},"winningOptionId":{"type":["null","string"],"pattern":"^opt_[0-9a-f]{32}$","description":"Persisted winner, matching this question’s options[].id. Null when undecided; rank never establishes a winner."},"averageScore":{"type":"number","description":"Star/emoji rating average. Only present for ratings, which have empty results.options and no winner."},"options":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["id","label","votes","score","percentage","rank","isWinner"],"properties":{"id":{"type":"string","pattern":"^opt_[0-9a-f]{32}$","description":"Public option ID matching this question’s options[].id."},"label":{"type":["null","string"]},"votes":{"type":"integer","description":"Option votes; final runoff votes for ranked questions; matchup wins for round-robin."},"score":{"type":["null","number"],"description":"Votes / totalResponses × 100, rounded to 1 decimal for head-to-head/single-select/multi-select or 2 for ranked final tallies. Round-robin: persisted score, falling back to wins / (wins + losses) × 100 rounded to 2 decimals; null with no matchups. Multi-select scores need not sum to 100."},"percentage":{"type":["null","integer"],"description":"Whole-number rounding of the unrounded votes / totalResponses × 100 ratio. Round-robin rounds its score instead; null when that score is null."},"rank":{"type":["null","integer"],"description":"Persisted rank when available; otherwise score descending, then label ascending (unscored options last)."},"isWinner":{"type":"boolean","description":"True only for the persisted winningOptionId."}}}}}}}}},"cost":{"type":["null","object"],"required":["object","amount","currency","credits","subtotalCredits","discounts","planRequired","quotedAt"],"properties":{"object":{"type":"string","enum":["quote"]},"amount":{"type":"number","description":"Total in USD after discounts and free plan credits."},"currency":{"type":"string","enum":["USD"]},"credits":{"type":"number","description":"Total in credits (1 credit = $2) after discounts."},"subtotalCredits":{"type":"number","description":"Pre-discount total in credits."},"discounts":{"type":"array","items":{"type":"object","required":["label","credits","amount"],"properties":{"label":{"type":"string"},"credits":{"type":"number"},"amount":{"type":"number"}}}},"planRequired":{"type":["null","string"],"description":"Plan tier this configuration requires (publish will fail until the owning team or personal user is on it), or null."},"quotedAt":{"type":"string","description":"When this quote was issued. Publish honours the most recent quote; if the price has moved since, publish answers 409 quote_changed."}},"description":"Cost quote for publishing this survey. null only when pricing is temporarily unavailable."},"creditBalance":{"type":["null","object"],"required":["object","credits","amount","currency","coversCost"],"properties":{"object":{"type":"string","enum":["credit_balance"]},"credits":{"type":"number","description":"Remaining credits."},"amount":{"type":"number","description":"Remaining balance in USD."},"currency":{"type":"string","enum":["USD"]},"coversCost":{"type":"boolean","description":"True when the balance covers `cost.credits`."}},"description":"The owning team or personal user’s current credit balance."}}}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/surveys/{id}/publish":{"post":{"operationId":"publishSurvey","summary":"Publish a survey (spend)","tags":["Surveys"],"description":"Pays for and publishes a draft survey in one call. Requires a regular key with `spend` scope or a partner key, and an `Idempotency-Key` header.\nPartner keys must send partnerPayment (paymentId and integer amountCents). This settles only against the owning team or personal user’s credits, never a saved card or automatic reload. Identical payments replay safely; a different payment on a paid survey returns 409 already_paid.\n\nWithout partnerPayment, regular team keys use this payment order: the quote from GET /v2/surveys/{id} is honoured or the call fails `409 quote_changed`; credits are spent first (oldest expiry first); any shortfall is charged off-session to the team's default saved card on the survey order itself (no credit-pack order is created).\n\n`200` means PAID — `paidAt` is set and collection starts asynchronously. Watch `status` on GET /v2/surveys/{id} (`in-progress` → `completed`) or the `survey.completed` webhook. `402` means nothing was charged and the survey is still a draft; see the response for how to fund it. A 402 never binds the Idempotency-Key — publish again with a new key after funding.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"partnerPayment":{"type":"object","additionalProperties":false,"required":["paymentId","amountCents"],"properties":{"paymentId":{"type":"string","minLength":1,"maxLength":255,"pattern":"\\S","description":"Stable partner payment reference. Reuse the same reference and amount on retry."},"amountCents":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Attested USD cents, exactly equal to the current quote."}}}}}}}},"parameters":[{"schema":{"type":"string","pattern":"^srv_[0-9a-f]{32}$"},"in":"path","name":"id","required":true,"description":"Canonical PickFu survey id (`srv_` plus 32 hexadecimal characters)."},{"schema":{"type":"string","minLength":1,"maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Unique key for this attempt. Reuse it only to retry the identical request."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["object","status","orderId","creditsUsed","cardCharged","amount","currency","survey"],"properties":{"object":{"type":"string","enum":["survey_publish"]},"status":{"type":"string","enum":["paid"],"description":"Paid, not yet live — collection starts asynchronously."},"orderId":{"type":"string","description":"The survey order that was paid (GET /v2/orders/{id})."},"creditsUsed":{"type":"number","description":"Credits deducted from the owning team or personal user’s balance."},"cardCharged":{"type":"number","description":"USD charged to the saved card (0 when credits covered it)."},"amount":{"type":"number","description":"Total paid in USD."},"currency":{"type":"string","enum":["USD"]},"survey":{"type":"object","required":["object","id","name","status","country","currency","sampleSize","numResponses","targeting","reporting","questions","cost","creditBalance","createdAt","publishedAt","completedAt","resultsUrl"],"properties":{"object":{"type":"string","enum":["survey"]},"id":{"type":"string","pattern":"^srv_[0-9a-f]{32}$","description":"Canonical PickFu survey id (`srv_` plus 32 hexadecimal characters)."},"externalId":{"type":["null","string"]},"name":{"type":["null","string"]},"status":{"type":"string","enum":["unpublished","in-progress","completed","archived","deleted"],"description":"unpublished (draft) → in-progress (published, collecting) → completed. archived surveys are hidden from the default list."},"country":{"type":"string"},"currency":{"type":"string","description":"Currency respondents see, derived from `country`."},"sampleSize":{"type":"integer"},"numResponses":{"type":["null","integer"]},"targeting":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"publishedAt":{"type":["null","string"]},"completedAt":{"type":["null","string"]},"resultsUrl":{"type":"string","description":"PickFu results page for this survey (requires a PickFu login)."},"reporting":{"type":"array","items":{"type":"string"}},"reportingDefaultsApplied":{"type":"array","items":{"type":"string"},"description":"Reporting traits added for free by the plan, not requested explicitly."},"questions":{"type":"array","items":{"type":"object","required":["object","id","type","prompt","likertScale","context","options","results"],"properties":{"object":{"type":"string","enum":["question"]},"id":{"type":"string","pattern":"^(?:qst|srv)_[0-9a-f]{32}$","description":"Question id (pass back on PATCH to edit in place)."},"type":{"type":"string","enum":["head_to_head","ranked","open_ended","click_test","emoji_rating","star_rating","five_second_test","screen_recording","single_select","multi_select","likert","van_westendorp","round_robin"]},"prompt":{"type":"string"},"likertScale":{"type":["null","string"],"description":"Likert preset, or null for non-likert questions / custom scales."},"context":{"type":["null","object"],"properties":{"text":{"type":"string"},"mediaUrl":{"type":"string"}}},"options":{"type":"array","items":{"type":"object","required":["object","id","label"],"properties":{"object":{"type":"string","enum":["option"]},"id":{"type":"string","pattern":"^opt_[0-9a-f]{32}$","description":"Option id (pass back on PATCH to edit in place)."},"label":{"type":["null","string"],"description":"Display label assigned by PickFu (e.g. \"A\")."},"value":{"type":"string","description":"Text option content."},"mediaUrl":{"type":"string","description":"CDN URL of an image/video option."},"asin":{"type":"string"},"imageSet":{"type":"array","items":{"type":"string"}},"imageSetDirection":{"type":"string","enum":["vertical","horizontal","a-plus"]},"mockup":{"type":"object","required":["type"],"additionalProperties":false,"description":"Custom product mockup (no ASIN). `type` picks the rendering: `serp` (search result tile) or `listing` (product page).","properties":{"type":{"type":"string","enum":["serp","listing"]},"image":{"type":"string"},"title":{"type":"string"},"numRatings":{"type":"number"},"reviewScore":{"type":"number"},"rating":{"type":"number"},"reviews":{"type":"number"},"price":{"type":"number"},"currency":{"type":"string"}}},"product":{"type":"object","description":"Product details fetched for an `asin` option (read-only).","properties":{"asin":{"type":["null","string"]},"title":{"type":["null","string"]},"price":{"type":["null","number"]},"salePrice":{"type":["null","number"]},"currency":{"type":["null","string"]},"rating":{"type":["null","number"]},"reviews":{"type":["null","number"]},"image":{"type":["null","string"]}}}}}},"results":{"type":["null","object"],"additionalProperties":false,"required":["totalResponses","winningOptionId","options"],"description":"Persisted aggregates, including partial results while collecting. Null for unanswered or unsupported questions, or unavailable aggregates.","properties":{"totalResponses":{"type":"integer","description":"Eligible respondents, capped at the survey sample size when positive."},"winningOptionId":{"type":["null","string"],"pattern":"^opt_[0-9a-f]{32}$","description":"Persisted winner, matching this question’s options[].id. Null when undecided; rank never establishes a winner."},"averageScore":{"type":"number","description":"Star/emoji rating average. Only present for ratings, which have empty results.options and no winner."},"options":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["id","label","votes","score","percentage","rank","isWinner"],"properties":{"id":{"type":"string","pattern":"^opt_[0-9a-f]{32}$","description":"Public option ID matching this question’s options[].id."},"label":{"type":["null","string"]},"votes":{"type":"integer","description":"Option votes; final runoff votes for ranked questions; matchup wins for round-robin."},"score":{"type":["null","number"],"description":"Votes / totalResponses × 100, rounded to 1 decimal for head-to-head/single-select/multi-select or 2 for ranked final tallies. Round-robin: persisted score, falling back to wins / (wins + losses) × 100 rounded to 2 decimals; null with no matchups. Multi-select scores need not sum to 100."},"percentage":{"type":["null","integer"],"description":"Whole-number rounding of the unrounded votes / totalResponses × 100 ratio. Round-robin rounds its score instead; null when that score is null."},"rank":{"type":["null","integer"],"description":"Persisted rank when available; otherwise score descending, then label ascending (unscored options last)."},"isWinner":{"type":"boolean","description":"True only for the persisted winningOptionId."}}}}}}}}},"cost":{"type":["null","object"],"required":["object","amount","currency","credits","subtotalCredits","discounts","planRequired","quotedAt"],"properties":{"object":{"type":"string","enum":["quote"]},"amount":{"type":"number","description":"Total in USD after discounts and free plan credits."},"currency":{"type":"string","enum":["USD"]},"credits":{"type":"number","description":"Total in credits (1 credit = $2) after discounts."},"subtotalCredits":{"type":"number","description":"Pre-discount total in credits."},"discounts":{"type":"array","items":{"type":"object","required":["label","credits","amount"],"properties":{"label":{"type":"string"},"credits":{"type":"number"},"amount":{"type":"number"}}}},"planRequired":{"type":["null","string"],"description":"Plan tier this configuration requires (publish will fail until the owning team or personal user is on it), or null."},"quotedAt":{"type":"string","description":"When this quote was issued. Publish honours the most recent quote; if the price has moved since, publish answers 409 quote_changed."}},"description":"Cost quote for publishing this survey. null only when pricing is temporarily unavailable."},"creditBalance":{"type":["null","object"],"required":["object","credits","amount","currency","coversCost"],"properties":{"object":{"type":"string","enum":["credit_balance"]},"credits":{"type":"number","description":"Remaining credits."},"amount":{"type":"number","description":"Remaining balance in USD."},"currency":{"type":"string","enum":["USD"]},"coversCost":{"type":"boolean","description":"True when the balance covers `cost.credits`."}},"description":"The owning team or personal user’s current credit balance."}}}}}}}},"400":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Nothing was charged and the survey is still a draft. With partnerPayment, `insufficient_credits` means the owning team or personal user needs more credits; saved cards and automatic reload are never used. Without partnerPayment, regular team keys can buy a pack with POST /v2/orders, then publish again with a NEW Idempotency-Key. `card_declined` / `card_authentication_required`: the saved card could not be charged off-session — add or change the card via `billingPortalUrl`, then publish again.","content":{"application/json":{"schema":{"type":"object","required":["error","requestId"],"description":"Nothing was charged and the survey is still a draft. With partnerPayment, `insufficient_credits` means the owning team or personal user needs more credits; saved cards and automatic reload are never used. Without partnerPayment, regular team keys can buy a pack with POST /v2/orders, then publish again with a NEW Idempotency-Key. `card_declined` / `card_authentication_required`: the saved card could not be charged off-session — add or change the card via `billingPortalUrl`, then publish again.","properties":{"error":{"type":"object","additionalProperties":true,"required":["type","code","message","shortfall"],"properties":{"type":{"type":"string","enum":["card_error"]},"code":{"type":"string","enum":["insufficient_credits","card_declined","card_authentication_required"]},"message":{"type":"string"},"shortfall":{"type":"object","required":["credits","amount","currency"],"properties":{"credits":{"type":"number","description":"In credits (1 credit = $2)."},"amount":{"type":"number","description":"In USD."},"currency":{"type":"string","enum":["USD"]}},"description":"What credits do not cover."},"suggestedPack":{"type":"object","required":["object","key","credits","bonusCredits","amount","currency"],"properties":{"object":{"type":"string","enum":["credit_pack"]},"key":{"type":"string","enum":["credit_pack_25","credit_pack_250","credit_pack_500","credit_pack_1000","credit_pack_3000","credit_pack_5000","credit_pack_10000"],"description":"Pass as `pack` to POST /v2/orders."},"credits":{"type":"number","description":"Credits received, including bonus credits."},"bonusCredits":{"type":"number"},"amount":{"type":"number","description":"Price in USD."},"currency":{"type":"string","enum":["USD"]}},"description":"Smallest pack that covers the shortfall (insufficient_credits only). Buying it does not publish the survey."},"billingPortalUrl":{"type":"string","description":"Stripe Billing Portal to add or change the saved card (card_declined / card_authentication_required)."},"declineCode":{"type":"string","description":"Stripe's decline code when the card issuer gave one."}}},"requestId":{"type":"string"}}}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`quote_changed`: the price moved since the quote in GET /v2/surveys/{id}; nothing was charged. Fetch the survey again for the new quote, then publish with a new Idempotency-Key. Also used for `quote_required`, `survey_already_published`, `idempotency_mismatch` and `idempotency_in_flight`.","content":{"application/json":{"schema":{"type":"object","required":["error","requestId"],"description":"`quote_changed`: the price moved since the quote in GET /v2/surveys/{id}; nothing was charged. Fetch the survey again for the new quote, then publish with a new Idempotency-Key. Also used for `quote_required`, `survey_already_published`, `idempotency_mismatch` and `idempotency_in_flight`.","properties":{"error":{"type":"object","additionalProperties":true,"required":["type","code","message"],"properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string"},"message":{"type":"string"},"quotedCost":{"type":"object","required":["credits","amount","currency"],"properties":{"credits":{"type":"number","description":"In credits (1 credit = $2)."},"amount":{"type":"number","description":"In USD."},"currency":{"type":"string","enum":["USD"]},"quotedAt":{"type":"string"}}},"currentCost":{"type":"object","required":["credits","amount","currency"],"properties":{"credits":{"type":"number","description":"In credits (1 credit = $2)."},"amount":{"type":"number","description":"In USD."},"currency":{"type":"string","enum":["USD"]}}}}},"requestId":{"type":"string"}}}}}},"422":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/surveys/{id}/responses":{"get":{"operationId":"listSurveyResponses","summary":"List survey responses","tags":["Results"],"description":"Lists approved responses for a team-owned survey in collection order. Pass `nextCursor` back as `cursor` for the next page and keep the same `limit`. Requires the `read` scope.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"in":"query","name":"limit","required":false,"description":"Page size, 1–100. Defaults to 20."},{"schema":{"type":"string","minLength":1},"in":"query","name":"cursor","required":false,"description":"Opaque cursor from a previous response's `nextCursor`. Omit for the first page."},{"schema":{"type":"string","pattern":"^srv_[0-9a-f]{32}$"},"in":"path","name":"id","required":true,"description":"Canonical PickFu survey id."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["data","hasMore","nextCursor"],"properties":{"data":{"type":"array","items":{"type":"object","required":["id","questionId"],"properties":{"id":{"type":"string","pattern":"^rsp_[0-9a-f]{32}$","description":"Canonical PickFu response id."},"pinned":{"type":"boolean","description":"Whether the response is pinned"},"explanation":{"type":["string","null"],"description":"Written explanation from the respondent"},"translation":{"type":["string","null"],"description":"Translated explanation text"},"chosenOption":{"type":["string","null"],"description":"Label of the option chosen by the respondent"},"selectedOptions":{"type":["array","null"],"description":"Labels of options selected by the respondent for multi-select questions","items":{"type":"string"}},"score":{"type":["number","null"],"description":"Rating score (for emoji_rating, star_rating question types)"},"clicks":{"type":["array","null"],"description":"Click coordinates for click_test questions","items":{"type":"object","properties":{"number":{"type":"number","description":"Click sequence number"},"xCoord":{"type":"number","description":"X coordinate of the click"},"yCoord":{"type":"number","description":"Y coordinate of the click"}}}},"ranking":{"type":["array","null"],"description":"Respondent's ranking of options for ranked question types. Each entry pairs a rank (1 = top pick) with the option label.","items":{"type":"object","properties":{"rank":{"type":"number","description":"Rank position (1 = respondent's top pick)"},"option":{"type":["string","null"],"description":"Option label at this rank"}}}},"media":{"type":["string","null"],"description":"Media URL (for screen_recording question types)"},"mediaTranscription":{"type":["string","null"],"description":"Transcription of the recorded media"},"matchup":{"type":["object","null"],"description":"Head-to-head matchup pairing shown to the respondent","properties":{"option1":{"type":"string","description":"First option in the matchup"},"option2":{"type":"string","description":"Second option in the matchup"}}},"statements":{"type":["array","null"],"description":"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.","items":{"type":"object","properties":{"id":{"type":"string","description":"GUID of the statement (the question option)"},"statement":{"type":["string","null"],"description":"Statement text"},"value":{"type":"number","description":"Numeric value the respondent picked on the Likert scale (1..N for the configured scale)"}}}},"prices":{"type":["object","null"],"description":"Van Westendorp price-sensitivity responses. Four numeric prices the respondent provided for the four standardized PSM questions.","properties":{"tooCheap":{"type":"number","description":"Price at which the product would be so cheap that quality is questionable"},"bargain":{"type":"number","description":"Price at which the product would be a bargain"},"expensive":{"type":"number","description":"Price at which the product starts to feel expensive but is still worth considering"},"tooExpensive":{"type":"number","description":"Price at which the product would be too expensive to consider"}}},"respondent":{"type":"array","description":"Demographic traits of the respondent","items":{"type":"object","properties":{"trait":{"type":"string","description":"Trait category name"},"value":{"type":"string","description":"Trait value"}}}},"questionId":{"type":["null","string"],"pattern":"^(?:qst|srv)_[0-9a-f]{32}$","description":"Canonical PickFu id of the question this response answers."}}}},"hasMore":{"type":"boolean","description":"True when another page exists after this one."},"nextCursor":{"type":["null","string"],"description":"Pass as `cursor` to fetch the next page. `null` on the last page."}}}}}},"400":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/surveys/{id}/exports/csv":{"get":{"operationId":"exportSurveyCsv","summary":"Download survey responses as CSV","tags":["Results"],"description":"Streams the existing v2 CSV export as `text/csv`. The survey must be completed. This synchronous legacy export can time out for unusually large studies. Requires the `read` scope.","parameters":[{"schema":{"type":"string","pattern":"^srv_[0-9a-f]{32}$"},"in":"path","name":"id","required":true,"description":"Canonical PickFu survey id."}],"responses":{"200":{"description":"CSV export stream.","content":{"text/csv":{"schema":{"type":"string"}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/credit-balance":{"get":{"operationId":"getCreditBalance","summary":"Get the owner’s credit balance","tags":["Billing"],"description":"The owning team’s remaining credits (1 credit = $2 when spending) and the buckets they sit in, in the order they are spent: soonest expiry first, then oldest first (FIFO). Requires the `read` scope.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["object","credits","amount","currency","buckets"],"properties":{"object":{"type":"string","enum":["credit_balance"]},"credits":{"type":"number","description":"Total remaining credits."},"amount":{"type":"number","description":"Total remaining in USD."},"currency":{"type":"string","enum":["USD"]},"buckets":{"type":"array","items":{"type":"object","required":["object","credits","initialCredits","expiresAt","createdAt","description"],"properties":{"object":{"type":"string","enum":["credit_bucket"]},"credits":{"type":"number","description":"Remaining credits in this bucket."},"initialCredits":{"type":["null","number"]},"expiresAt":{"type":["null","string"],"description":"When the bucket expires; null never expires."},"createdAt":{"type":"string"},"description":{"type":["null","string"]}}}}}}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/orders":{"post":{"operationId":"createOrder","summary":"Buy a credit pack","tags":["Billing"],"description":"Creates an `open` credit-pack order and a Stripe-hosted Checkout Session; send the buyer to `checkoutUrl` to pay by card on Stripe's page. Credits land on the team once Stripe confirms the payment — poll `GET /v2/orders/{id}` until `status` is `paid`, or listen for the `order.paid` webhook. Buying credits never publishes a survey. Requires an `Idempotency-Key` header. Once checkout starts, the purchase identity and parameters are retained for 24 hours, including after failed responses. Reuse the key only to retry the identical request and resume the same order and Checkout Session; use a new key for a different purchase. Requires the `spend` scope.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["pack"],"additionalProperties":false,"properties":{"pack":{"type":"string","enum":["credit_pack_25","credit_pack_250","credit_pack_500","credit_pack_1000","credit_pack_3000","credit_pack_5000","credit_pack_10000"],"description":"Credit pack to buy — the same packs as the PickFu app."},"successUrl":{"type":"string","maxLength":2048,"description":"Where Stripe sends the buyer after paying. Defaults to the PickFu credits page."},"cancelUrl":{"type":"string","maxLength":2048,"description":"Where Stripe sends the buyer if they leave without paying. Defaults to the PickFu credits page."}}}}}},"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Unique key for this attempt. Reuse it only to retry the identical request."}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["object","id","type","status","amount","currency","pack","surveyId","checkoutUrl","createdAt","paidAt"],"properties":{"object":{"type":"string","enum":["order"]},"id":{"type":"string","pattern":"^ord_[0-9a-f]{32}$","description":"Canonical PickFu order id."},"type":{"type":"string","enum":["credit_pack","survey","other"],"description":"`credit_pack` orders buy credits; `survey` orders pay for publishing a survey (created with the survey, paid by publish)."},"status":{"type":"string","enum":["open","processing","paid","failed","canceled"],"description":"open → processing → paid. `canceled` when the Checkout Session expired or the payment attempt was abandoned; `failed` when the last card attempt was declined."},"amount":{"type":"number","description":"Total in USD."},"currency":{"type":"string"},"pack":{"type":["null","object"],"required":["object","key","credits","bonusCredits","amount","currency"],"properties":{"object":{"type":"string","enum":["credit_pack"]},"key":{"type":"string","enum":["credit_pack_25","credit_pack_250","credit_pack_500","credit_pack_1000","credit_pack_3000","credit_pack_5000","credit_pack_10000"],"description":"Pass as `pack` to POST /v2/orders."},"credits":{"type":"number","description":"Credits received, including bonus credits."},"bonusCredits":{"type":"number"},"amount":{"type":"number","description":"Price in USD."},"currency":{"type":"string","enum":["USD"]}}},"surveyId":{"type":["null","string"]},"checkoutUrl":{"type":["null","string"],"description":"Stripe-hosted payment page for an open credit-pack order. Valid for 24 hours after the order was created; null afterwards, for survey orders, and in list responses."},"createdAt":{"type":"string"},"paidAt":{"type":["null","string"]}}}}}},"400":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"listOrders","summary":"List orders","tags":["Billing"],"description":"The team's orders — credit packs and survey orders — newest first, as a cursor page. `checkoutUrl` is null in lists; fetch the order for it. Requires the `read` scope.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"in":"query","name":"limit","required":false,"description":"Page size, 1–100. Defaults to 20."},{"schema":{"type":"string","minLength":1},"in":"query","name":"cursor","required":false,"description":"Opaque cursor from a previous response's `nextCursor`. Omit for the first page."},{"schema":{"type":"string","enum":["credit_pack","survey"]},"in":"query","name":"type","required":false,"description":"Only orders of this type. Default: both."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["data","hasMore","nextCursor"],"properties":{"data":{"type":"array","items":{"type":"object","required":["object","id","type","status","amount","currency","pack","surveyId","checkoutUrl","createdAt","paidAt"],"properties":{"object":{"type":"string","enum":["order"]},"id":{"type":"string","pattern":"^ord_[0-9a-f]{32}$","description":"Canonical PickFu order id."},"type":{"type":"string","enum":["credit_pack","survey","other"],"description":"`credit_pack` orders buy credits; `survey` orders pay for publishing a survey (created with the survey, paid by publish)."},"status":{"type":"string","enum":["open","processing","paid","failed","canceled"],"description":"open → processing → paid. `canceled` when the Checkout Session expired or the payment attempt was abandoned; `failed` when the last card attempt was declined."},"amount":{"type":"number","description":"Total in USD."},"currency":{"type":"string"},"pack":{"type":["null","object"],"required":["object","key","credits","bonusCredits","amount","currency"],"properties":{"object":{"type":"string","enum":["credit_pack"]},"key":{"type":"string","enum":["credit_pack_25","credit_pack_250","credit_pack_500","credit_pack_1000","credit_pack_3000","credit_pack_5000","credit_pack_10000"],"description":"Pass as `pack` to POST /v2/orders."},"credits":{"type":"number","description":"Credits received, including bonus credits."},"bonusCredits":{"type":"number"},"amount":{"type":"number","description":"Price in USD."},"currency":{"type":"string","enum":["USD"]}}},"surveyId":{"type":["null","string"]},"checkoutUrl":{"type":["null","string"],"description":"Stripe-hosted payment page for an open credit-pack order. Valid for 24 hours after the order was created; null afterwards, for survey orders, and in list responses."},"createdAt":{"type":"string"},"paidAt":{"type":["null","string"]}}}},"hasMore":{"type":"boolean","description":"True when another page exists after this one."},"nextCursor":{"type":["null","string"],"description":"Pass as `cursor` to fetch the next page. `null` on the last page."}}}}}},"400":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/orders/{id}":{"get":{"operationId":"getOrder","summary":"Get an order","tags":["Billing"],"description":"Returns a credit-pack or survey order. For an open credit-pack order `checkoutUrl` is the Stripe page to pay on while the session is valid (24h). Requires the `read` scope.","parameters":[{"schema":{"type":"string","pattern":"^ord_[0-9a-f]{32}$"},"in":"path","name":"id","required":true,"description":"Canonical PickFu order id."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["object","id","type","status","amount","currency","pack","surveyId","checkoutUrl","createdAt","paidAt"],"properties":{"object":{"type":"string","enum":["order"]},"id":{"type":"string","pattern":"^ord_[0-9a-f]{32}$","description":"Canonical PickFu order id."},"type":{"type":"string","enum":["credit_pack","survey","other"],"description":"`credit_pack` orders buy credits; `survey` orders pay for publishing a survey (created with the survey, paid by publish)."},"status":{"type":"string","enum":["open","processing","paid","failed","canceled"],"description":"open → processing → paid. `canceled` when the Checkout Session expired or the payment attempt was abandoned; `failed` when the last card attempt was declined."},"amount":{"type":"number","description":"Total in USD."},"currency":{"type":"string"},"pack":{"type":["null","object"],"required":["object","key","credits","bonusCredits","amount","currency"],"properties":{"object":{"type":"string","enum":["credit_pack"]},"key":{"type":"string","enum":["credit_pack_25","credit_pack_250","credit_pack_500","credit_pack_1000","credit_pack_3000","credit_pack_5000","credit_pack_10000"],"description":"Pass as `pack` to POST /v2/orders."},"credits":{"type":"number","description":"Credits received, including bonus credits."},"bonusCredits":{"type":"number"},"amount":{"type":"number","description":"Price in USD."},"currency":{"type":"string","enum":["USD"]}}},"surveyId":{"type":["null","string"]},"checkoutUrl":{"type":["null","string"],"description":"Stripe-hosted payment page for an open credit-pack order. Valid for 24 hours after the order was created; null afterwards, for survey orders, and in list responses."},"createdAt":{"type":"string"},"paidAt":{"type":["null","string"]}}}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/billing-portal-sessions":{"post":{"operationId":"createBillingPortalSession","summary":"Open the Stripe Billing Portal","tags":["Billing"],"description":"Creates a short-lived Stripe Billing Portal session for the team's Stripe customer, where a card can be added, changed or removed. The saved default card is what `POST /v2/surveys/{id}/publish` charges when credits fall short. Card numbers never touch PickFu. Requires the `spend` scope.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"returnUrl":{"type":"string","maxLength":2048,"description":"Where Stripe sends the user when they leave the portal. Defaults to the PickFu account settings page."}}}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["object","url"],"properties":{"object":{"type":"string","enum":["billing_portal_session"]},"url":{"type":"string","description":"Send the user here. The link expires after a few minutes."}}}}}},"400":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhook-endpoints":{"post":{"operationId":"createWebhookEndpoint","summary":"Create a webhook endpoint","tags":["Webhooks"],"description":"Creates a webhook endpoint for the key’s owning team. The response returns its signing secret once. Deliveries use `X-PickFu-Signature: t=<unix-seconds>,v1=<hex-hmac>` where the signed message is `<t>.<raw-body>`. Requires the `write` scope.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["url","enabledEvents"],"properties":{"url":{"type":"string","format":"uri","description":"Public HTTPS destination that receives webhook POST deliveries."},"enabledEvents":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","enum":["survey.completed","order.paid"]}}}}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["object","id","url","enabledEvents","createdAt","updatedAt","secret"],"properties":{"object":{"type":"string","enum":["webhook_endpoint"]},"id":{"type":"string"},"url":{"type":"string","format":"uri"},"enabledEvents":{"type":"array","items":{"type":"string","enum":["survey.completed","order.paid"]},"uniqueItems":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"secret":{"type":"string","description":"Signing secret. Returned only when the endpoint is created; store it securely."}}}}}},"400":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"listWebhookEndpoints","summary":"List webhook endpoints","tags":["Webhooks"],"description":"Lists webhook endpoints for the key’s owning team. Requires the `read` scope.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"in":"query","name":"limit","required":false,"description":"Page size, 1–100. Defaults to 20."},{"schema":{"type":"string","minLength":1},"in":"query","name":"cursor","required":false,"description":"Opaque cursor from a previous response's `nextCursor`. Omit for the first page."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["data","hasMore","nextCursor"],"properties":{"data":{"type":"array","items":{"type":"object","required":["object","id","url","enabledEvents","createdAt","updatedAt"],"properties":{"object":{"type":"string","enum":["webhook_endpoint"]},"id":{"type":"string"},"url":{"type":"string","format":"uri"},"enabledEvents":{"type":"array","items":{"type":"string","enum":["survey.completed","order.paid"]},"uniqueItems":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}}}},"hasMore":{"type":"boolean","description":"True when another page exists after this one."},"nextCursor":{"type":["null","string"],"description":"Pass as `cursor` to fetch the next page. `null` on the last page."}}}}}},"400":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhook-endpoints/{id}":{"get":{"operationId":"getWebhookEndpoint","summary":"Get a webhook endpoint","tags":["Webhooks"],"description":"Gets one webhook endpoint belonging to the key’s owning team. Requires the `read` scope.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true,"description":"Webhook endpoint id."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["object","id","url","enabledEvents","createdAt","updatedAt"],"properties":{"object":{"type":"string","enum":["webhook_endpoint"]},"id":{"type":"string"},"url":{"type":"string","format":"uri"},"enabledEvents":{"type":"array","items":{"type":"string","enum":["survey.completed","order.paid"]},"uniqueItems":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}}}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateWebhookEndpoint","summary":"Update a webhook endpoint","tags":["Webhooks"],"description":"Replaces the supplied URL and/or enabled event set. Requires the `write` scope.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"url":{"type":"string","format":"uri","description":"Public HTTPS destination that receives webhook POST deliveries."},"enabledEvents":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","enum":["survey.completed","order.paid"]}}}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true,"description":"Webhook endpoint id."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["object","id","url","enabledEvents","createdAt","updatedAt"],"properties":{"object":{"type":"string","enum":["webhook_endpoint"]},"id":{"type":"string"},"url":{"type":"string","format":"uri"},"enabledEvents":{"type":"array","items":{"type":"string","enum":["survey.completed","order.paid"]},"uniqueItems":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}}}}}},"400":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteWebhookEndpoint","summary":"Delete a webhook endpoint","tags":["Webhooks"],"description":"Permanently removes a webhook endpoint. Requires the `write` scope.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true,"description":"Webhook endpoint id."}],"responses":{"204":{"description":"Default Response"},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/media":{"post":{"operationId":"uploadMedia","summary":"Host an image or video","tags":["Media"],"description":"Uploads an image or video to the PickFu CDN and returns the URL to use as an option `mediaUrl` or question `context.mediaUrl`. You can also pass any public URL directly as `mediaUrl` — it is re-hosted when the survey is saved — so this endpoint is only needed for files you cannot host yourself. YouTube, Vimeo and Wistia URLs are returned unchanged (`embedded: true`). Requires the `write` scope.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"url":{"type":"string","format":"uri","description":"Public http(s) URL of the image or video to host. Mutually exclusive with `data`."},"data":{"type":"string","description":"Base64-encoded file contents (max 20 MB decoded). Mutually exclusive with `url`."},"filename":{"type":"string","maxLength":255,"description":"Optional filename hint."},"resourceType":{"type":"string","enum":["image","video"],"description":"Defaults to image."}},"oneOf":[{"required":["url"]},{"required":["data"]}]}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["object","url","resourceType","embedded"],"properties":{"object":{"type":"string","enum":["media"]},"url":{"type":"string","description":"CDN URL to use as `mediaUrl`."},"resourceType":{"type":"string","enum":["image","video"]},"embedded":{"type":"boolean","description":"True when the URL is an embeddable video platform link returned as-is."}}}}}},"400":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/traits":{"get":{"operationId":"listTraits","summary":"List targeting traits","tags":["Traits"],"description":"Lists the audience traits a survey can target, for one respondent country. The whole list fits in one page (`hasMore` is always false). Audience-size estimates are not available. Requires the `read` scope.","parameters":[{"schema":{"type":"string","minLength":2,"maxLength":2},"in":"query","name":"country","required":false,"description":"Two-letter country code; returns the traits available for that respondent country. Defaults to US."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["data","hasMore","nextCursor"],"properties":{"data":{"type":"array","items":{"type":"object","required":["object","permalink","name"],"properties":{"object":{"type":"string","enum":["trait"]},"permalink":{"type":"string","description":"Stable identifier to put in a survey's `targeting` array."},"name":{"type":"string","description":"Human-readable label, \"Category: value\" (e.g. \"Age: 25-34\")."}}}},"hasMore":{"type":"boolean","description":"True when another page exists after this one."},"nextCursor":{"type":["null","string"],"description":"Pass as `cursor` to fetch the next page. `null` on the last page."}}}}}},"400":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope returned by every non-2xx /v2 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"servers":[{"url":"https://api.pickfu.com/v2","description":"Production"},{"url":"https://api.pickfu-sandbox.com/v2","description":"Sandbox"}],"security":[{"apiKey":[]}],"tags":[{"name":"Embed","description":"Partner-only, team- and customer-bound iframe sessions."},{"name":"Me","description":"Identify the calling key and the team it acts for."},{"name":"Surveys","description":"Draft, edit, read, list and stop surveys. A survey is edited as a whole (PATCH replaces the fields you send); every read includes a cost quote that publish will honour. Publishing and paying are separate endpoints."},{"name":"Media","description":"Host images and videos on the PickFu CDN for use as option or context `mediaUrl`s."},{"name":"Traits","description":"Audience traits available for survey `targeting`, per respondent country."},{"name":"Billing","description":"Credit balance, credit-pack orders paid on Stripe-hosted Checkout, survey orders, and the Stripe Billing Portal for saved cards. PickFu never handles card numbers."},{"name":"Results","description":"Cursor-paged response data and the existing synchronous CSV export."},{"name":"Webhooks","description":"Team endpoints, signed delivery, and the survey.completed and order.paid launch events."}]}