Skip to main content

How It Works

After initiating an upload, you receive a presigned upload_url and a set of required_headers. Send a PUT request with your file binary to that URL, including all required headers exactly as provided.
If any header is missing or doesn’t match, the upload will be rejected with HTTP 403 Forbidden. The presigned URL is cryptographically bound to the exact headers returned.

Request

PUT {upload_url from Initiate Upload response}
Include every key-value pair from required_headers as HTTP headers on the PUT request.

Code Examples

curl -X PUT \
  -H "Content-Type: audio/mp4" \
  -H "x-amz-meta-file-id: 550e8400-e29b-41d4-a716-446655440000" \
  -H "x-amz-meta-expires-at: 2026-09-01T10:00:00Z" \
  -H "x-amz-meta-audio-duration-seconds: 1008.32" \
  -H "x-amz-tagging: uploader=customer&ttl_days=30" \
  --data-binary @audio.m4a \
  "https://s3.us-east-1.amazonaws.com/bucket/uploads/550e8400.m4a?X-Amz-Algorithm=..."

Response

A successful upload returns HTTP 200 OK with an empty body.
HTTP StatusMeaning
200File uploaded successfully
403Missing or mismatched headers — ensure all required_headers are included exactly
400File too large or malformed request

Full Example

Here’s the complete flow — initiate, upload, then check status:
import requests
import time

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://storage-service.core.eachlabs.run"

# Step 1: Initiate
init_response = requests.post(
    f"{BASE_URL}/api/v1/uploads",
    headers={
        "Content-Type": "application/json",
        "X-API-Key": API_KEY
    },
    json={
        "owner": "org-your-org-id",
        "uploader": "customer",
        "file_type": "audio",
        "content_type": "audio/mp4"
    }
)
data = init_response.json()
file_id = data["id"]

# Step 2: Upload
with open("audio.m4a", "rb") as f:
    requests.put(data["upload_url"], headers=data["required_headers"], data=f)

# Step 3: Poll until complete
while True:
    status_response = requests.get(
        f"{BASE_URL}/api/v1/uploads/{file_id}",
        headers={"X-API-Key": API_KEY}
    )
    status = status_response.json()

    if status["status"] in ("COMPLETED", "ERROR"):
        break

    print(f"Status: {status['status']}...")
    time.sleep(3)

print(f"Done! Status: {status['status']}, Size: {status.get('size_bytes')} bytes")
If you provided a callback_url when initiating the upload, you’ll receive a webhook notification when processing completes — no polling needed.
Last modified on April 15, 2026