Skip to main content

When to use this

An image prompt is a question that shows the candidate an image while they record their video answer. Use it when you want candidates to react to something visual: a system architecture diagram, a chart, a design mockup, or a product photo. See Image Prompt Questions for how it looks in the dashboard. Through the API, an image prompt is a regular QA step with questionFormat.image set. The candidate still answers on video, so answerFormat.video is required as usual.
Send exactly one image per question. The schema accepts 1–10 images in questionFormat.image, but the dashboard and the candidate experience currently support only one. If you send more, candidates only see the first image. Support for multiple images per question may come later. If you want candidates to react to several images, create a separate image prompt question for each one.

Before you start

Don’t forget to send your Hireflix API Key in the X-API-KEY header to https://api.hireflix.com/me. Check the image first:
  • File type – any image/* MIME type: JPEG, PNG, GIF, WebP, or SVG. Anything else is rejected with MediaFileUploadMimeTypeNotSupportedError.
  • File size – 10 MB max per image, which is exactly 10,000,000 bytes. Note this is a decimal megabyte, unlike the 150 MB video limit (157,286,400 bytes). Larger files are rejected with MediaFileUploadTooBigError.
  • Dimensions – no width or height limit.

How image uploads differ from video uploads

Videos and images are uploaded in completely different ways, so don’t reuse the video flow from Creating a Position.

How a multipart upload works

A normal GraphQL request is a JSON body. JSON can’t carry a binary file, so the API follows the GraphQL multipart request spec: instead of JSON, you send a multipart/form-data request (the same format a browser uses for an HTML form with a file input) made of three parts.
1

operations: the mutation, with a placeholder for the file

A form field named operations that holds the JSON you’d normally send as the request body: query plus variables. Declare the file as a variable of type Upload! and set its value to null. The null is a placeholder that the server replaces with the uploaded file.
2

map: which file goes where

A form field named map that tells the server which file part fills which variable. The key is the name of the file part ("0"), and the value is a list of paths into operations.
This reads as: “put the file from form field 0 into variables.image”. The path matches the variable name you declared in step 1. The mutation wraps that variable in a list itself (upload: [$image]), so the path doesn’t need a list index.
3

The file itself

A file part whose name matches the key in map (here 0), containing the raw image bytes and an image/* content type.
Put together, the request body looks like this on the wire:
You never write this by hand. Every HTTP client has a form-data helper that builds it for you, as the examples below show. But it’s worth knowing what’s in it, because each rule below maps to a common error.
Common pitfalls:
  • Order matters: operations first, then map, then the file. Otherwise the request fails with Misordered multipart fields; files should follow 'map'.
  • operations and map must be plain text fields, not file parts. In curl, -F 'operations=@operations.json' sends the JSON as a file, which triggers the same “misordered” error. Use -F 'operations=<operations.json' instead (< reads the file’s contents into a text field).
  • Always set the file’s content type explicitly (image/png, image/jpeg, image/webp, …). The server checks the content type declared on the file part, not the file contents. Some clients only guess it from the file extension: curl, for example, sends a .webp file as application/octet-stream, which is rejected with MediaFileUploadMimeTypeNotSupportedError.
  • Don’t set the request’s Content-Type header yourself. Let your HTTP client set it. It adds the boundary=... value that separates the parts; if you hard-code multipart/form-data without it, the server can’t parse the body.
  • Alias message when you request several error types. The error types don’t all declare message the same way, so requesting message on more than one of them fails validation with Fields "message" conflict because they return conflicting types. Give each one an alias, like tooBigMessage: message, as the examples below do.

Create a position with an image prompt

This is the full mutation used in every example below. It creates a position with one image prompt question:
  • questionFormat.image.upload – the image file, passed as an Upload! variable. Always exactly one.
  • answerFormat.video – required, because video is currently the only answer format. {} uses the position defaults; see Creating a Position for the available settings.
  • title / description – same as any other question. The image is shown alongside them.
Send it with any HTTP client that supports multipart form data:
Save the operations JSON to a file, since the query is awkward to escape inline:
operations.json
Then send the three parts, in order:
  • operations=<operations.json reads the file’s contents into a text field. Don’t use @ here.
  • 0=@diagram.png attaches the image as a file part named 0, matching the key in map.
  • ;type=image/png sets the file’s content type. Change it to match your image (image/jpeg, image/webp, …).
  • -F makes curl send multipart/form-data and set the Content-Type header with the boundary, so don’t add one yourself.
A successful response looks like this:

Reuse an image with mediaIds

Every uploaded image gets a media ID. To use the same image in another question or position, pass that ID in mediaIds instead of uploading the file again. No upload means no multipart request: this is a normal JSON request. Fetch the media ID of an image that’s already on a position. Replace <position-id> with the ID of the position that has the image, for example the data.id returned when you created it:
Image questions come back as AdminQAImageContent, and images[].id is the media ID. Video questions come back as AdminQAVideoContent, and text-only questions have questionFormat: null. Then reference it in mediaIds:
  • Use either upload or mediaIds in one question, with one image in total.
  • The ID must belong to an image. A video’s media ID returns MediaMimeTypeMismatchError.
  • The 10 MB limit applies to existing media as well.

Image questions in updatePosition

updatePosition accepts the same questionFormat.image input, with both upload and mediaIds. To add a new image question to an existing position, send the same multipart request as above, but with updatePosition and a $image: Upload! variable.
steps in updatePosition fully replaces every question on the position. When you resend an existing image question, include its image again with questionFormat: { image: { mediaIds: ["<image-media-id>"] } }. If you leave out questionFormat, the question is saved as a text-only question and the image is removed. Fetch the current media IDs first with the query above.
This example keeps an existing image question and adds a new one with a freshly uploaded image, in a single multipart request (map stays {"0": ["variables.image"]}):

Handling the response

createPosition and updatePosition return a union type. For image questions, these are the cases to handle:
  • MutationCreatePositionSuccess / MutationUpdatePositionSuccess – the position was saved; read it from data.
  • ValidationError – one or more fields failed validation; inspect fieldErrors for the offending path and message.
  • MediaFileUploadTooBigError – the uploaded image is over 10,000,000 bytes. For example: File "big.png" exceeds maximum allowed size of 10000000 bytes.
  • MediaFileUploadMimeTypeNotSupportedError – the file part’s content type isn’t image/*. For example: File "diagram.webp" has unsupported mime type "application/octet-stream". Supported types are: image/*. If the file really is an image, set its content type explicitly (see How a multipart upload works).
  • MediaNotFoundError – a media ID in mediaIds doesn’t exist or doesn’t belong to your company.
  • MediaMimeTypeMismatchError – a media ID in mediaIds isn’t an image.
A malformed multipart request (wrong part order, operations sent as a file) fails before GraphQL runs, and returns a plain JSON object with a message such as Misordered multipart fields; files should follow 'map' instead of a data object.

Key reminders

  • One image per question. The schema accepts up to 10, but candidates only see the first one. Use one image prompt question per image.
  • Images are uploaded inside the mutation request as multipart/form-data, not through a reserve/presigned-URL flow like videos.
  • Part order: operations, map, then the file. operations and map are text fields.
  • Set the file’s content type explicitly to an image/* type. Don’t set the request’s Content-Type header yourself.
  • Max 10 MB (10,000,000 bytes) per image, any image/* type, no dimension limit.
  • answerFormat.video is required on every image question.
  • Reuse images with mediaIds, and always resend questionFormat for existing image questions in updatePosition, or the image is dropped.
  • Verify the result by opening admin.hireflix.com/jobs, finding the position by name, and previewing the interview.

Learn next?

Need to create many positions at once? Let’s learn how to bulk-create positions with a script.