---
title: "Verify a presigned-URL upload landed in S3"
method: POST
path: "/v1/apps/{app_id}/deploy/verify-upload"
tags: ["Apps"]
---

# Verify a presigned-URL upload landed in S3

`POST /v1/apps/{app_id}/deploy/verify-upload`

Confirm a bundle uploaded via a presigned PUT URL actually landed in S3.

S3 returns an empty body on a successful PUT — the ETag is only in
response headers, which browser clients often can't read due to CORS.
This endpoint does a server-side HEAD and returns {etag, size_bytes,
last_modified, exists} so clients can verify the upload before calling
`/deploy`.

## Path parameters

- `app_id` string, required

## Request body

- VerifyUploadRequest
  - `bundle_s3_key` string, required — S3 key returned by /deploy/upload-url — the object you just PUT to.

## Response `200`

Successful Response

- VerifyUploadResponse — Confirms a presigned-URL upload landed in S3. S3 presigned PUTs return an empty body — the ETag is only in response headers, which many clients can't surface (CORS restrictions, browser DevTools gotchas). This endpoint does a server-side HEAD on the bundle and returns the object metadata so clients can verify their upload succeeded.
  - `bundle_s3_key` string, required — S3 key that was verified
  - `exists` boolean, required — True if the object exists in S3
  - `etag` string, nullable — S3 ETag of the uploaded object (MD5 for single-part uploads).
  - `size_bytes` integer, nullable — Size of the uploaded object in bytes.
  - `last_modified` string, nullable — Last-modified timestamp (ISO 8601).

## Other responses

- `400` — Bad Request
- `401` — Unauthorized
- `403` — Forbidden
- `404` — Not Found
- `422` — Validation Error
- `500` — Internal Server Error

---

[API](https://skmtc.net/mixpeek/apis/mixpeek-api.md) · [All operations](https://skmtc.net/mixpeek/apis/mixpeek-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/mixpeek/mixpeek-api/versions/220a3b263fda/schema)
