Media uploads
Upload bytes through OpenPost's storage-safe HTTP flow.
Media uploads usually use three requests. Your integration creates an upload session, sends bytes to its storage target, then completes the session. If OpenPost finds identical ready media in the workspace, it returns deduped: true and the returned media_id is ready to use without the upload and completion requests. The examples use Bash, curl, and jq.
1. Create a session
OPENPOST_BASE="https://app.openpo.st"
WORKSPACE_ID="ws_..."
SESSION_JSON="$(curl "$OPENPOST_BASE/api/v1/media/upload-session" \
-X POST \
-H "Authorization: Bearer $OPENPOST_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: cover-2026-09-19" \
--data "$(jq -n \
--arg workspace_id "$WORKSPACE_ID" \
--arg filename "cover.png" \
--arg mime_type "image/png" \
--arg alt_text "Product import screen with the completed file list" \
'{workspace_id: $workspace_id, filename: $filename, mime_type: $mime_type, size: 48231, alt_text: $alt_text}')")"
MEDIA_ID="$(printf '%s' "$SESSION_JSON" | jq -r '.media_id')"
DEDUPED="$(printf '%s' "$SESSION_JSON" | jq -r '.deduped')"The response includes media_id, deduped, complete_url, and an upload object with a method, URL, and headers. The following steps only run when DEDUPED is false.
2. Send the bytes
Use the exact method and headers from upload. An absolute target is external storage, so do not send the OpenPost bearer token to it. A relative target is an authenticated OpenPost endpoint.
if [ "$DEDUPED" = "false" ]; then
UPLOAD_METHOD="$(printf '%s' "$SESSION_JSON" | jq -r '.upload.method')"
UPLOAD_URL="$(printf '%s' "$SESSION_JSON" | jq -r '.upload.url')"
UPLOAD_HEADERS=()
while IFS= read -r header; do
UPLOAD_HEADERS+=(-H "$header")
done < <(printf '%s' "$SESSION_JSON" | jq -r '.upload.headers | to_entries[] | "\(.key): \(.value)"')
case "$UPLOAD_URL" in
http://* | https://*)
UPLOAD_TARGET="$UPLOAD_URL"
UPLOAD_EXTERNAL=true
;;
/*)
UPLOAD_TARGET="$OPENPOST_BASE$UPLOAD_URL"
UPLOAD_EXTERNAL=false
;;
*)
printf 'Unsupported upload URL: %s\n' "$UPLOAD_URL" >&2
exit 1
;;
esac
if [ "$UPLOAD_EXTERNAL" = "true" ]; then
curl "$UPLOAD_TARGET" \
-X "$UPLOAD_METHOD" \
"${UPLOAD_HEADERS[@]}" \
--data-binary @cover.png
else
curl "$UPLOAD_TARGET" \
-X "$UPLOAD_METHOD" \
-H "Authorization: Bearer $OPENPOST_TOKEN" \
"${UPLOAD_HEADERS[@]}" \
--data-binary @cover.png
fi
fi3. Complete the session
Complete the session with the returned media_id, your workspace ID, and your OpenPost bearer token. Completion starts the normal validation and media-processing flow.
if [ "$DEDUPED" = "false" ]; then
RESULT_JSON="$(curl "$OPENPOST_BASE/api/v1/media/upload-session/$MEDIA_ID/complete" \
-X POST \
-H "Authorization: Bearer $OPENPOST_TOKEN" \
-H "Content-Type: application/json" \
--data "{\"workspace_id\":\"$WORKSPACE_ID\"}")"
MEDIA_ID="$(printf '%s' "$RESULT_JSON" | jq -r '.id')"
fiUse the completion response's id in Publication or Rendition media fields. When the create-session response is deduplicated, use its media_id instead and skip steps 2 and 3.
The create upload session reference contains the exact request and response schemas and links to completion. JavaScript and TypeScript integrations can use media.upload() to run all three steps.