Vieunite.Developers
Open quickstart

Recommended integration

Add the hosted Picker.

Vieunite provides the discovery and selection experience. Your CMS controls editor authentication, tenant credentials, persistence and rendering.

How it works

CMS browserCalls your session bridgeCMS login + CSRF
CMS backendCreates a Picker sessionTenant token stays here
Hosted PickerBrowses and selectsTemporary capability only
CMS backendRedeems the selectionSource-backed references returned

The browser SDK owns the modal or popup, source validation, message protocol, cancellation and one-time capability handling. Application code never receives the launch or selection capability.

Browser SDK

Load the major-version URL and create one reusable client:

cms-editor.htmlJavaScript
<script src="https://collection.vieunite.com/sdk/v1/picker.js"></script>

const picker = VieunitePicker.create({
  sessionEndpoint: "/api/vieunite/picker/session",
  redeemEndpoint: "/api/vieunite/picker/redeem",
  pickerOrigin: "https://collection.vieunite.com",
  presentation: "modal",
});

pickerOrigin is the exact trusted origin allowed to provide picker_url. It defaults to the SDK script origin; set it explicitly when the script is self-hosted.

Open options

open a selectionJavaScript
const result = await picker.open({
  allowedTypes: ["artwork"],
  selectionMode: "multiple",
  maxSelection: 0,
  initialFilters: {
    resource_type: ["image"],
    orientations: ["portrait", "square"],
  },
  expiresIn: 600,
});
OptionDefaultMeaning
allowedTypes["artwork"]artwork, collection, or both.
selectionMode"single"Single or multiple selection.
maxSelection10 means unlimited in multiple mode, subject to the completion hard limit.
initialFilters{}Initial discovery state only—not an authorization constraint.
expiresIn600Session lifetime from 60 to 900 seconds.
signalNoneAn optional AbortSignal.

Result

open() resolves only after your CMS backend successfully redeems the selection.

selectedJavaScript
const result = {
  status: "selected",
  protocolVersion: 2,
  sessionId: "pks_12345",
  selection: [{
    provider: "vieunite-art-collection",
    type: "artwork",
    id: "art_456",
    rendition: "crop"
  }]
};
cancelledJavaScript
const result = {
  status: "cancelled",
  reason: "user_closed",
  sessionId: "pks_12345",
  selection: []
};

Closing the Picker is a normal cancelled result, not an exception. An artwork reference always includes its selected rendition; a collection reference never does.

CMS backend bridge

The endpoints below live on your CMS origin. They authenticate the editor, enforce CSRF, constrain request options and call Vieunite with the tenant token. The browser request deliberately has no host_capabilities; the trusted CMS backend adds that immutable declaration.

CMS backend bridgeJavaScript
const API = "https://collection.vieunite.com";
const TOKEN = process.env.VIEUNITE_TENANT_TOKEN;
const CMS_ORIGIN = new URL(process.env.CMS_PUBLIC_URL).origin;

// Server-only helper. TOKEN must never be returned to browser code.
async function vieunite(path, body, headers = {}) {
  const response = await fetch(`${API}${path}`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${TOKEN}`,
      "Content-Type": "application/json",
      ...headers,
    },
    body: JSON.stringify(body),
  });
  const payload = await response.json();
  if (!response.ok) throw Object.assign(new Error(payload.detail), {
    status: response.status,
    requestId: response.headers.get("X-Request-ID"),
  });
  return payload;
}

// requireEditor verifies the CMS login; requireCsrf verifies a CMS CSRF token.
app.post("/api/vieunite/picker/session", requireEditor, requireCsrf,
  async (req, res) => {
    // Allowlist browser input. Never proxy req.body directly to Vieunite.
    const selectionMode = req.body.selection_mode === "multiple"
      ? "multiple" : "single";
    const payload = await vieunite("/v1/picker/sessions", {
      allowed_types: ["artwork"],
      selection_mode: selectionMode,
      max_selection: selectionMode === "multiple" ? 0 : 1,
      default_filters: {
        resource_type: ["image"],
        orientations: ["portrait", "square"],
      },
      host_capabilities: { credit_display: false },
      expires_in: 600,
      callback_origin: CMS_ORIGIN,
    });
    // Persist the session-to-editor binding in your CMS database.
    await bindSession(payload.data.session_id, req.user.id);
    res.json(payload);
  });

app.post("/api/vieunite/picker/redeem", requireEditor, requireCsrf,
  async (req, res) => {
    // Reject redemption unless this editor owns the Picker session.
    await assertSessionOwner(req.body.session_id, req.user.id);
    const payload = await vieunite(
      "/v1/picker/selections/redeem",
      {
        session_id: req.body.session_id,
        selection_code: req.body.selection_code,
      },
      { "Idempotency-Key": `picker:${req.body.session_id}` },
    );
    res.json(payload);
  });

Orientation and credit policy

default_filters.orientations accepts portrait, landscape and square. Multiple values are ORed; omitted, null and [] mean All. The Picker user can change this browsing filter without losing the current selection.

Orientation is calculated from each rendition's delivery dimensions. A rendition is square when abs(width - height) / max(width, height) <= 0.03; source artwork and Preview thumbnail dimensions are not used. The Picker labels its direction-matching choice Recommended version, not “Best Fit”, because no target size or fit mode was supplied.

Presentation and lifecycle

The default modal presentation is responsive, keyboard accessible and isolated with Shadow DOM. Set presentation: "popup" only when a separate browser window fits an existing CMS workflow better.

  • picker.open(options)Open or focus the active flow. Repeated calls share the same Promise.
  • picker.focus()Focus the active modal or popup.
  • picker.close()
  • picker.cancel()Resolve the active flow as cancelled.
  • picker.destroy()Remove listeners, timers and DOM during permanent page teardown.
  • picker.on(name, handler)Subscribe to statechange, complete, cancel or error.

Picker errors

Operational failures reject with VieunitePicker.PickerError and a stable code.

invalid_configinvalid_optionsbrowser_token_forbiddenpopup_blockedinvalid_picker_originsession_request_failedinvalid_sessionredemption_failedinvalid_redemptionpicker_timeoutpicker_destroyed

Unknown sources, origins, event names, protocol versions and session IDs are ignored. Log the SDK error code and related server X-Request-ID, but never capability values.

Search documentation

Start with “Picker”, “rendition”, or an endpoint path.