Insights Company

Stimuli

Upload images and videos to a STIMULI project and assign them to interview questions.

Stimuli are media assets — images or videos — shown to participants alongside specific interview questions. They are only available on projects created with type: "STIMULI".

All stimuli endpoints accept a project_id. The project_id is returned when you create a project.

To inspect uploaded assets at any time, use GET /v1/stimuli — it returns every stimuli record for a project including its ID, image/video URL, caption, display order, and current question assignments.

The upload workflow is:

  1. Presign — request a short-lived S3 upload URL for each file.
  2. Upload — POST the file directly to S3 using the presigned URL.
  3. Confirm — register the uploaded file with Persona to create the stimuli record.
  4. List — call GET /v1/stimuli to retrieve stimuli_id values for your uploaded assets.
  5. Assign — link each asset to the questions it should appear alongside.

All stimuli must have at least one question assigned before the project can be launched.

Text-only stimulus questions

A stimulus doesn't have to carry media. To add a plain question with no image or video — for example a standalone question between two media stimuli — call Assign without a stimuli_id. Persona creates the stimulus record for you and returns its stimuli_id. Skip the presign, upload, and confirm steps entirely.

This is also how you edit questions on a STIMULI project — PATCH /v1/projects/{project_id} rejects questions for stimuli projects.


List stimuli

Returns all uploaded stimuli for a project, ordered by display position. Use this to retrieve stimuli_id values for the assign endpoint.

Returns all uploaded stimuli for a project, ordered by display order.

Authentication

x-api-key header

Query Parameters

NameTypeDescription
project_id*stringThe project UUID.

Headers

NameTypeDescription
x-api-keystring—

Responses

FieldTypeDescription
itemsrequired
object[]—
↳stimuli_idrequired
stringStimuli Id
↳display_orderrequired
integerDisplay Order
↳image_urlrequired
stringImage Url
↳video_urlrequired
stringVideo Url
↳captionrequired
string—
↳question_itemsrequired
string[]Question Items
totalrequired
integer—

Example request

GET /v1/stimuli
curl "https://external-api.prod.insightscompany.io/v1/stimuli?project_id=<project_id>" \
-H "x-api-key: <api_key>"

Query parameters

ParameterTypeRequiredDescription
project_idUUIDYesThe project UUID

Example response

{
  "items": [
    {
      "stimuli_id": "a1b2c3d4-...",
      "display_order": 1,
      "image_url": "https://cdn.example.com/stimuli/...",
      "video_url": null,
      "caption": "Homepage redesign concept",
      "question_items": [
        { "question": "What do you notice first?", "probing_level": "low", "instructions": null },
        { "question": "How does this make you feel?", "probing_level": "medium", "instructions": null }
      ]
    }
  ],
  "total": 1
}
FieldDescription
stimuli_idUse this in the Assign endpoint
display_orderThe order assets are shown to participants
question_itemsQuestions this asset is currently assigned to. Each is { question, probing_level, instructions } — probing_level is low unless set explicitly; instructions is null unless set

Presign an upload

Returns a presigned S3 POST URL and form fields for a direct upload. Call Confirm after the upload completes.

Returns a presigned S3 POST URL and form fields. POST a multipart/form-data request with the fields followed by the file, then call /stimuli/confirm to create the stimuli record.

Authentication

x-api-key header

Headers

NameTypeDescription
x-api-keystring—

Request Bodyrequired

FieldTypeDescription
project_idrequired
stringThe project UUID.
filenamerequired
stringOriginal filename including extension.
content_typerequired
stringMIME type of the file.
file_size_bytes
integerFile size in bytes, used for pre-validation.
file_id
stringOptional pre-assigned UUID for the stimuli asset.

Responses

FieldTypeDescription
upload_urlrequired
stringUpload Url
upload_fieldsrequired
objectUpload Fields
file_idrequired
stringFile Id
s3_keyrequired
stringS3 Key
media_urlrequired
stringMedia Url
expires_inrequired
integerExpires In
content_typerequired
stringContent Type

Example request

POST /v1/stimuli/presign
curl -X POST "https://external-api.prod.insightscompany.io/v1/stimuli/presign" \
-H "x-api-key: <api_key>" \
-H "Content-Type: application/json" \
-d '{
  "project_id": "<project_id>",
  "filename": "homepage-concept.png",
  "content_type": "image/png"
}'

Request body

FieldTypeRequiredDescription
project_idUUIDYesThe project UUID
filenamestringYesOriginal filename including extension
content_typestringYesMIME type of the file (e.g. image/png, video/mp4)
file_size_bytesintegerNoFile size in bytes — used for pre-validation before the upload
file_idUUIDNoPre-assigned UUID for the stimuli asset. A new UUID is generated if omitted

Example response

// request sent image/png — the API stores it as webp
{
  "upload_url": "https://s3.amazonaws.com/...",
  "upload_fields": {
    "Content-Type": "image/webp",
    "key": "stimuli/prod/<interview_id>/<file_id>.webp",
    "...": "..."
  },
  "file_id": "a1b2c3d4-...",
  "s3_key": "stimuli/prod/<interview_id>/<file_id>.webp",
  "media_url": "https://cdn.example.com/stimuli/<file_id>.webp",
  "expires_in": 3600,
  "content_type": "image/webp"
}
FieldDescription
upload_urlThe S3 endpoint to POST the file to
upload_fieldsForm fields that must be included in the multipart upload, before the file
file_idSave this — required in the Confirm call
s3_keyThe final object key. Its extension is the stored format — see the note below
media_urlThe CDN URL the stored asset will be served from
content_typeThe stored MIME type — may differ from what you sent
expires_inSeconds until the presigned URL expires

JPEG and PNG uploads are stored as WebP. The API transcodes .jpg / .jpeg / .png to .webp (.gif, .webp, and .mp4 are kept as-is). The presign response's s3_key, content_type, and media_url reflect the stored format. When you call Confirm, file_ext must be the extension from the presign s3_key (e.g. .webp), not your original filename's extension — otherwise confirm returns 404 "Uploaded file not found in storage".

Uploading to S3

POST the file to upload_url as multipart/form-data. Include every entry from upload_fields as a form field first (in the order returned), then the file as the last field named file:

curl -X POST "<upload_url>" \
  -F "Content-Type=<upload_fields.Content-Type>" \
  -F "key=<upload_fields.key>" \
  -F "policy=<upload_fields.policy>" \
  -F "x-amz-algorithm=<...>" \
  -F "x-amz-credential=<...>" \
  -F "x-amz-date=<...>" \
  -F "x-amz-security-token=<...>" \
  -F "x-amz-signature=<...>" \
  -F "file=@homepage-concept.png"

The file field carries your original bytes; the object is stored under the resolved key from s3_key (e.g. .webp for a JPEG/PNG upload). A successful upload returns HTTP 204 No Content. Then call Confirm.


Confirm an upload

Verifies the file exists in storage and creates the stimuli record.

Call after uploading the file to the presigned URL. Creates a Stimuli record attached to the interview.

Authentication

x-api-key header

Headers

NameTypeDescription
x-api-keystring—

Request Bodyrequired

FieldTypeDescription
project_idrequired
stringThe project UUID.
file_idrequired
stringThe file_id returned from the presign step.
file_extrequired
stringThe file extension (e.g. .webp, .mp4).
caption
stringOptional caption shown below the media.

Responses

FieldTypeDescription
messagerequired
string—
file_idrequired
stringFile Id
media_urlrequired
stringMedia Url

Example request

POST /v1/stimuli/confirm
curl -X POST "https://external-api.prod.insightscompany.io/v1/stimuli/confirm" \
-H "x-api-key: <api_key>" \
-H "Content-Type: application/json" \
-d '{
  "project_id": "<project_id>",
  "file_id": "<file_id>",
  "file_ext": ".webp",
  "caption": "Homepage redesign concept"
}'

Request body

FieldTypeRequiredDescription
project_idUUIDYesThe project UUID
file_idUUIDYesThe file_id returned from the presign step
file_extstringYesThe stored file extension, including the leading dot — take it from the presign response's s3_key (a JPEG/PNG upload becomes .webp). Using your original filename's extension here returns 404 "Uploaded file not found in storage"
captionstringNoCaption shown below the media. Can also be set or updated later via the Assign endpoint

Example response

{
  "message": "Upload finalized",
  "file_id": "a1b2c3d4-...",
  "media_url": "https://cdn.example.com/stimuli/..."
}

Assign stimuli to questions

Sets which questions a stimuli asset appears alongside during the interview. Pass an empty question_items list to clear all assignments.

Called without a stimuli_id, it instead creates a new text-only stimulus question (no media) and returns its stimuli_id. Only valid on STIMULI projects.

Sets which questions a stimuli asset appears alongside during the interview. Pass an empty list to clear all assignments. All stimuli must have at least one question assigned before the project can be launched.

Authentication

x-api-key header

Headers

NameTypeDescription
x-api-keystring—

Request Bodyrequired

FieldTypeDescription
project_idrequired
stringThe project UUID.
stimuli_idrequired
stringThe stimuli UUID to assign.
question_itemsrequired
string[]Question texts this stimuli should appear alongside. Use exact strings from the interview.
caption
stringOptional caption shown below the media. Send null to clear an existing caption.

Responses

FieldTypeDescription
stimuli_idrequired
stringStimuli Id
question_itemsrequired
string[]Question Items
captionrequired
string—

Example request

POST /v1/stimuli/assign
curl -X POST "https://external-api.prod.insightscompany.io/v1/stimuli/assign" \
-H "x-api-key: <api_key>" \
-H "Content-Type: application/json" \
-d '{
  "project_id": "<project_id>",
  "stimuli_id": "<stimuli_id>",
  "question_items": [
    "What do you notice first?",
    { "question": "How does this make you feel?", "probing_level": "high" }
  ],
  "caption": "Homepage redesign concept"
}'

Request body

FieldTypeRequiredDescription
project_idUUIDYesThe project UUID
stimuli_idUUIDNoThe stimuli UUID to update — from the confirm response or the list endpoint. Omit to create a new text-only stimulus question (no media)
question_items(string | object)[]YesQuestions this asset should appear alongside. Each entry is a plain question string, or an object { question, probing_level, instructions }. Question text is trimmed and blank entries are dropped. Pass an empty array to clear all assignments (ignored when creating)
question_items[].questionstringYesThe exact question text shown to participants
question_items[].probing_levelstringNolow, medium, or high (case-insensitive) — how hard the AI interviewer probes on that question. Defaults to low (no follow-ups). A bare string entry is stored as low. See probing levels
question_items[].instructionsstringNoPrivate interviewer guidance for this question — how to probe, what to listen for. Never shown to participants. Omitted from storage when not set
captionstringNoCaption shown below the media. Omit to leave an existing caption unchanged

Example response

question_items is always returned in object form, with probing_level resolved and instructions as null when unset:

{
  "stimuli_id": "a1b2c3d4-...",
  "question_items": [
    { "question": "What do you notice first?", "probing_level": "low", "instructions": null },
    { "question": "How does this make you feel?", "probing_level": "high", "instructions": "Probe for the emotional reaction, not the design critique." }
  ],
  "caption": "Homepage redesign concept"
}

Create a text-only question

Omit stimuli_id to add a standalone question with no media. The new stimulus is appended after existing ones and its stimuli_id is returned — use that same ID with this endpoint later to edit the question text or clear it.

POST /v1/stimuli/assign
curl -X POST "https://external-api.prod.insightscompany.io/v1/stimuli/assign" \
-H "x-api-key: <api_key>" \
-H "Content-Type: application/json" \
-d '{
  "project_id": "<project_id>",
  "question_items": ["Before we look at anything — what matters most to you here?"]
}'
{
  "stimuli_id": "e5f6a7b8-...",
  "question_items": [
    { "question": "Before we look at anything — what matters most to you here?", "probing_level": "low", "instructions": null }
  ],
  "caption": null
}

Errors

StatuscodeCause
404not_foundThe project, or the given stimuli_id, was not found for your organisation
409survey_aware_guide_requiredThe stimulus holds survey questions — edit it through the survey workflow
409not_a_stimuli_projectTried to create a text-only question (no stimuli_id) on a non-STIMULI project
400empty_question_itemsTried to create a text-only question with an empty question_items list

Notes

  • Supported image formats: PNG, JPEG, GIF, WebP. Maximum 10 MB. PNG and JPEG are stored as WebP — the presign response's s3_key / content_type / media_url reflect the stored format, and file_ext on Confirm must match it. GIF and WebP are stored as-is.
  • Supported video format: MP4. Maximum 100 MB.
  • The project_id is the id field returned when you create a project.

On this page