Skip to content
OpenPostDocs
OpenPostDocs

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
fi

3. 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')"
fi

Use 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.

On this page