Skip to content

Uploading Media

This guide covers how to upload selfies, liveness frames, and identity documents to a ProofAge verification session. It includes file requirements, HMAC signing for multipart requests, validation error handling, and what constitutes a complete submission.

Media Types

Every upload to POST /v1/verifications/{id}/media requires a type field that identifies the kind of media being uploaded.

TypePurposeAdditional fields
selfiePrimary selfie photo used for age estimation and face matching.None
liveness_selfieA single frame from the liveness check sequence.head_turn_step (optional, integer 0–10)
documentA photo of an identity document.side (required), document (required)

Document Types and Sides

When type=document, you must also specify which document you are uploading and which side:

Document typeAccepted document valueRequired sides
National ID cardidfront and back
Driver's licensedriver_licensefront and back
Passportpassportfront only
Residence permitresidence_permitfront and back

File Requirements

ConstraintValue
Field namefile
Content typeAny image format (image/jpeg, image/png, etc.)
Maximum size10 MB
Encodingmultipart/form-data

Request Format

All uploads use multipart/form-data. The form fields are:

FieldTypeRequiredDescription
filefileAlwaysThe image file to upload.
typestringAlwaysOne of: selfie, liveness_selfie, document.
sidestringIf type=documentOne of: front, back.
documentstringIf type=documentOne of: id, driver_license, passport, residence_permit.
fingerprintstringNoOptional device fingerprint, max 64 characters.
head_turn_stepintegerNoLiveness step index (0–10). Only relevant for liveness_selfie.

HMAC Signing for Multipart Uploads

Multipart requests use a different canonical string format than JSON requests because the raw multipart body is not deterministic. See the Authentication guide for the full specification.

The canonical string is built as:

UPPERCASE_METHOD + path + '\n' + sortedFormFields + '\n' + sortedFileHashes

Step-by-step:

  1. Method and path — Concatenate the uppercase HTTP method with the request path:

    POST/v1/verifications/ver_abc123def456/media
  2. Sorted form fields — Take all non-file fields (type, side, document, fingerprint, head_turn_step), sort their keys alphabetically, and serialize them using RFC 3986 query string encoding:

    document=id&side=front&type=document
  3. Sorted file hashes — Compute the SHA-256 hash of each uploaded file's raw bytes, sort the hashes alphabetically, and join with commas:

    a3f2b8c1d4e5...7890abcdef12345678
  4. Combine with newlines:

    POST/v1/verifications/ver_abc123def456/media
    document=id&side=front&type=document
    a3f2b8c1d4e5...7890abcdef12345678
  5. Sign — Compute HMAC-SHA256 of the canonical string using your secret key and hex-encode the result.

WARNING

Only non-file form fields go into sortedFormFields. The file field is represented by its SHA-256 hash in sortedFileHashes.

Selfie Validation Errors

When a selfie or liveness selfie fails validation, the API returns 422 Unprocessable Entity with an error code. Handle these in your UI to guide the user toward a successful upload.

Error codeDescriptionSuggested user message
FACE_NOT_FOUNDNo face was detected in the image."We couldn't detect a face. Please make sure your face is clearly visible and centered."
FACE_INVALID_SIZEThe face is too small or too large relative to the frame."Your face is too far away or too close. Please hold your device at arm's length."
FACE_TOO_BLURRYThe image is not sharp enough for analysis."The photo is blurry. Please hold your device steady and make sure the camera is focused."
FACE_BRIGHTNESS_INVALIDThe image is too dark or overexposed."The lighting is not right. Please move to a well-lit area and avoid direct sunlight behind you."
FACE_OCCLUDEDPart of the face is covered (sunglasses, mask, hand, etc.)."Something is covering your face. Please remove sunglasses, masks, or hats and try again."
FACE_ALIGNMENT_FAILEDThe face landmarks could not be extracted reliably."We had trouble analyzing your photo. Please face the camera directly and try again."
FACE_TOO_TILTEDThe head is rotated too far from a frontal position."Please look straight at the camera without tilting your head."
FACE_VALIDATION_FAILEDA generic validation failure that does not fit the above categories."We couldn't process your photo. Please take a new photo in good lighting with your face clearly visible."

Document Validation Errors

When a document photo fails validation, the API returns 422 Unprocessable Entity with an error code.

Error codeDescriptionSuggested user message
DOCUMENT_NOT_FOUNDNo document was detected in the image."We couldn't find a document in your photo. Please place the document on a flat surface and take a clear photo."
DOCUMENT_TOO_BLURRYThe document image is not sharp enough to read."The document photo is blurry. Please hold your device steady and make sure the text is readable."
DOCUMENT_BRIGHTNESS_INVALIDThe document image is too dark or washed out."The lighting is not right. Avoid glare and shadows, and photograph the document in even lighting."
DOCUMENT_NOT_READABLEThe document text or photo cannot be extracted."We couldn't read the document. Make sure all four corners are visible and there is no glare on the surface."
DOCUMENT_VALIDATION_FAILEDA generic document validation failure."We couldn't process your document. Please take a new photo with the full document visible and in focus."

Ready for Submission

Before calling POST /v1/verifications/{id}/submit, all required media must be uploaded. The requirements depend on the verification flow:

FlowRequired media
Age (selfie-only)selfie
KYC (documents + liveness)selfie + document front + document back

TIP

Passports only have a front side. For passport-based KYC verifications, you need a selfie and a document front — no back is required.

If you attempt to submit before all required media is present, the API returns 422 with a message indicating which media is missing.

Re-upload Behavior

Uploading a new file of the same type and side replaces the previous upload. For example:

  • Uploading a second selfie replaces the first selfie.
  • Uploading a second document with side=front and document=id replaces the previous front of the ID.

This means users can fix a rejected photo without creating a new verification session. Re-uploading after a resubmission_requested status moves the verification back to started.

Examples

Upload a Selfie

bash
curl -X POST https://api.proofage.com/v1/verifications/ver_abc123def456/media \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-HMAC-Signature: YOUR_HMAC_SIGNATURE" \
  -F "type=selfie" \
  -F "[email protected]"

Response (201 Created):

json
{
  "data": {
    "id": "med_xyz789",
    "verification_id": "ver_abc123def456",
    "type": "selfie",
    "mime_type": "image/jpeg",
    "created_at": "2026-03-19T12:02:00Z"
  }
}

Upload a Document (Front of ID)

bash
curl -X POST https://api.proofage.com/v1/verifications/ver_abc123def456/media \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-HMAC-Signature: YOUR_HMAC_SIGNATURE" \
  -F "type=document" \
  -F "side=front" \
  -F "document=id" \
  -F "file=@id_front.jpg"

Response (201 Created):

json
{
  "data": {
    "id": "med_doc456",
    "verification_id": "ver_abc123def456",
    "type": "document",
    "side": "front",
    "document": "id",
    "mime_type": "image/jpeg",
    "created_at": "2026-03-19T12:03:00Z"
  }
}

Upload a Document (Back of ID)

bash
curl -X POST https://api.proofage.com/v1/verifications/ver_abc123def456/media \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-HMAC-Signature: YOUR_HMAC_SIGNATURE" \
  -F "type=document" \
  -F "side=back" \
  -F "document=id" \
  -F "file=@id_back.jpg"

Validation Error Response

If the selfie fails validation, the API returns 422:

json
{
  "error": {
    "code": "FACE_TOO_BLURRY",
    "message": "The selfie image is too blurry for analysis."
  }
}

If consent has not been accepted, the API returns 403:

json
{
  "error": {
    "code": "CONSENT_REQUIRED",
    "message": "User consent must be recorded before uploading media."
  }
}

See the Consent Handling guide for how to resolve this.

Next Steps

ProofAge Developer Documentation