curl --request POST \
--url https://api.mixpeek.com/v1/buckets/{bucket_identifier}/uploads/{upload_id}/confirm \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'X-Namespace: <api-key>' \
--data '
{
"etag": "d41d8cd98f00b204e9800998ecf8427e",
"file_size_bytes": 52428800
}
'import requests
url = "https://api.mixpeek.com/v1/buckets/{bucket_identifier}/uploads/{upload_id}/confirm"
payload = {
"etag": "d41d8cd98f00b204e9800998ecf8427e",
"file_size_bytes": 52428800
}
headers = {
"Authorization": "Bearer <token>",
"X-Namespace": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
Authorization: 'Bearer <token>',
'X-Namespace': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({etag: 'd41d8cd98f00b204e9800998ecf8427e', file_size_bytes: 52428800})
};
fetch('https://api.mixpeek.com/v1/buckets/{bucket_identifier}/uploads/{upload_id}/confirm', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.mixpeek.com/v1/buckets/{bucket_identifier}/uploads/{upload_id}/confirm",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'etag' => 'd41d8cd98f00b204e9800998ecf8427e',
'file_size_bytes' => 52428800
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"X-Namespace: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.mixpeek.com/v1/buckets/{bucket_identifier}/uploads/{upload_id}/confirm"
payload := strings.NewReader("{\n \"etag\": \"d41d8cd98f00b204e9800998ecf8427e\",\n \"file_size_bytes\": 52428800\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("X-Namespace", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.mixpeek.com/v1/buckets/{bucket_identifier}/uploads/{upload_id}/confirm")
.header("Authorization", "Bearer <token>")
.header("X-Namespace", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"etag\": \"d41d8cd98f00b204e9800998ecf8427e\",\n \"file_size_bytes\": 52428800\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.mixpeek.com/v1/buckets/{bucket_identifier}/uploads/{upload_id}/confirm")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["X-Namespace"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"etag\": \"d41d8cd98f00b204e9800998ecf8427e\",\n \"file_size_bytes\": 52428800\n}"
response = http.request(request)
puts response.read_body{
"completed_at": "2024-01-15T10:35:00Z",
"description": "Synchronous confirmation with object creation",
"etag": "d41d8cd98f00b204e9800998ecf8427e",
"file_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"file_size_bytes": 52428800,
"object_id": "obj_xyz789",
"status": "COMPLETED",
"upload_id": "upl_abc123",
"verified_at": "2024-01-15T10:35:00Z"
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}Confirm Upload
Verify S3 upload completion and create bucket object.
After uploading to S3 using the presigned URL, call this endpoint to:
- Verify the file exists in S3
- Validate ETag and file size (if provided)
- Create bucket object (default, unless create_object_on_confirm=false)
- Update upload status to COMPLETED
Processing: the created object is PENDING and is not processed by
confirmation. Add it to a batch and submit the batch to start processing,
or set auto_submit_batch=true on this request to create and submit a
batch for the new object in the same call.
Sync vs Async:
- Files < 100MB: Processed synchronously (~100ms)
- Files >= 100MB or async=true: Processed asynchronously (returns task_id)
Duplicate Detection:
- If file hash matches existing upload, marks as duplicate
- References original object_id if available
Unique Keys:
- If the bucket has a unique_key, the object is created under an insertion
policy:
?policy=insert|update|upserton this request, else the bucket’sunique_key.default_policy. With neither, confirmation returns 422.
curl --request POST \
--url https://api.mixpeek.com/v1/buckets/{bucket_identifier}/uploads/{upload_id}/confirm \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'X-Namespace: <api-key>' \
--data '
{
"etag": "d41d8cd98f00b204e9800998ecf8427e",
"file_size_bytes": 52428800
}
'import requests
url = "https://api.mixpeek.com/v1/buckets/{bucket_identifier}/uploads/{upload_id}/confirm"
payload = {
"etag": "d41d8cd98f00b204e9800998ecf8427e",
"file_size_bytes": 52428800
}
headers = {
"Authorization": "Bearer <token>",
"X-Namespace": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
Authorization: 'Bearer <token>',
'X-Namespace': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({etag: 'd41d8cd98f00b204e9800998ecf8427e', file_size_bytes: 52428800})
};
fetch('https://api.mixpeek.com/v1/buckets/{bucket_identifier}/uploads/{upload_id}/confirm', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.mixpeek.com/v1/buckets/{bucket_identifier}/uploads/{upload_id}/confirm",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'etag' => 'd41d8cd98f00b204e9800998ecf8427e',
'file_size_bytes' => 52428800
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"X-Namespace: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.mixpeek.com/v1/buckets/{bucket_identifier}/uploads/{upload_id}/confirm"
payload := strings.NewReader("{\n \"etag\": \"d41d8cd98f00b204e9800998ecf8427e\",\n \"file_size_bytes\": 52428800\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("X-Namespace", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.mixpeek.com/v1/buckets/{bucket_identifier}/uploads/{upload_id}/confirm")
.header("Authorization", "Bearer <token>")
.header("X-Namespace", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"etag\": \"d41d8cd98f00b204e9800998ecf8427e\",\n \"file_size_bytes\": 52428800\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.mixpeek.com/v1/buckets/{bucket_identifier}/uploads/{upload_id}/confirm")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["X-Namespace"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"etag\": \"d41d8cd98f00b204e9800998ecf8427e\",\n \"file_size_bytes\": 52428800\n}"
response = http.request(request)
puts response.read_body{
"completed_at": "2024-01-15T10:35:00Z",
"description": "Synchronous confirmation with object creation",
"etag": "d41d8cd98f00b204e9800998ecf8427e",
"file_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"file_size_bytes": 52428800,
"object_id": "obj_xyz789",
"status": "COMPLETED",
"upload_id": "upl_abc123",
"verified_at": "2024-01-15T10:35:00Z"
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}{
"error": {
"details": {
"id": "ns_123",
"resource": "namespace"
},
"message": "Namespace not found",
"type": "NotFoundError"
},
"status": 404,
"success": false
}Authorizations
Mixpeek API key, sent as Authorization: Bearer mxp_sk_.... Create one in Studio under Settings → API Keys, or with an admin key via POST /v1/organizations/users/{user_email}/api-keys. A missing, malformed, unknown, revoked, inactive or expired key returns 401; a key without the permission, scope or plan an operation needs returns 403.
Namespace id (ns_...), not the namespace name. This scopes the request rather than authenticating it, and it is required on every operation marked x-mixpeek-namespace-scoped.
Path Parameters
The unique identifier of the bucket
The unique identifier of the upload
Query Parameters
Process confirmation asynchronously (recommended for files >= 100MB)
Insertion policy for unique key enforcement on the object created at confirmation. Valid values: 'insert', 'update', 'upsert'. Only applies if the bucket has a unique_key. Overrides the bucket's default_policy, matching ?policy= on POST /objects. Required when the bucket has a unique_key and no default_policy.
insert, update, upsert "insert"
Body
Keys this body does not declare are ignored, and the response's warnings name them, until 2026-11-02; from 2026-11-02 they are refused with 422 extra_forbidden naming the accepted keys.
Request to confirm S3 upload completion and create bucket object.
⚠️ THIS ENDPOINT IS REQUIRED AFTER UPLOADING TO S3!
S3 presigned URLs have no callback mechanism - the API cannot detect when your upload completes. You MUST call this endpoint to finalize the upload.
Why confirmation is required: - S3 doesn't notify us when uploads complete - We need to verify the file actually exists in S3 - We need to create the bucket object - We need to update quotas and tracking
The system will:
- Verify the S3 object exists (HeadObject call)
- Validate ETag matches (if provided) - RECOMMENDED for integrity
- Validate file size matches (if provided)
- Create bucket object (default, unless create_object_on_confirm=false)
- Update upload status to COMPLETED
If you don't call confirm: - Upload stays in PENDING status - No bucket object is created - File is orphaned in S3
S3 ETag returned from the upload. OPTIONAL but RECOMMENDED. After uploading to S3, the response includes an ETag header. Providing this ensures the file wasn't corrupted during upload. If provided and doesn't match S3's ETag, confirmation will fail with error. Format: Usually an MD5 hash, may be enclosed in quotes.
"d41d8cd98f00b204e9800998ecf8427e"
Actual file size uploaded, in bytes. OPTIONAL but RECOMMENDED. If provided, will be validated against the actual S3 object size. Mismatch indicates upload corruption or network issues. If not provided, size validation is skipped.
x >= 110485760
If true, automatically create AND submit a batch containing the newly created object after confirmation. The returned task_id will be the submitted batch's task id, and batch_id will be populated. Eliminates the need for separate POST /batches and POST /batches/{id}/submit calls during bulk ingest. Has no effect if create_object_on_confirm is false or the object is a duplicate. Defaults to false for backwards compatibility — existing flows that batch many objects into one submission still work.
Response
Successful Response
Response from upload confirmation.
Upload ID that was confirmed
Updated upload status (COMPLETED or PROCESSING)
PENDING, QUEUED, IN_PROGRESS, PROCESSING, COMPLETED, COMPLETED_WITH_ERRORS, FAILED, CANCELED, INTERRUPTED, UNKNOWN, SKIPPED, DRAFT, ACTIVE, ARCHIVED, SUSPENDED, DEACTIVATED S3 ETag from uploaded object
Actual file size from S3
File content hash (from ETag)
When verification completed
When upload completed
Created bucket object ID (if create_object_on_confirm was true)
Task ID for async processing. Populated when auto_submit_batch=true (holds the submitted batch's first-tier task id) or when async=true.
Batch ID created and submitted by auto_submit_batch=true. Use it to poll status via GET /v1/buckets/{id}/batches/{batch_id}.
Confirmation message
Was this page helpful?

