Gondola integration — video posts end to end

Gondola is Energixer's companion service: a multi-tenant social/forum backend. Gondola stores references to Energixer videos and never holds your Energixer key — the integration is one shared webhook secret.

The flow

  1. Upload (your backend + the device): POST /v1/videos on Energixer → hand the upload_token to the device → device uploads via tus.

  2. Post immediately (your backend → Gondola): create the post with video: {energixer_video_id} while the video is still processing. Gondola's video_post_visibility config decides whether the post shows a processing placeholder or stays hidden until ready.

  3. Readiness (Energixer → Gondola, direct): point your Energixer domain's webhook_url at Gondola's receiver — https://<your-gondola>/v1/webhooks/energixer — and paste the same whsec_ secret into both dashboards:

    • Energixer: PATCH /v1/domain/configwebhook_url, webhook_secret
    • Gondola: domain setting energixer_webhook_secret

    Gondola verifies the t=/v1= HMAC signature, deduplicates by Energixer-Delivery-Id, matches the video by (domain, energixer_video_id), stores the playback URLs from the payload, and flips the post live. On video.failed, Gondola applies your configured fallback (publish text-only vs. keep hidden).

  4. Playback: Gondola hands your clients the Energixer URLs in its feed payloads; devices stream directly from Energixer. Gondola never proxies video bytes.

    Those URLs need a playback key. Energixer videos are private by default, so a device that fetches a feed URL with no credential gets 401 playback_key_required. Gondola deliberately holds no ek_ key, which means it cannot mint one for you — minting is your backend's job. See the pattern below.

Playback keys: one per viewer session

Mint one domain-scoped key per viewer session. A key covers the whole domain and lives an hour by default, so one covers every card in the feed, every page of pagination, and every prefetch after it. That is one extra call per session — not per card, not per page. The client's only ongoing job is re-minting when it expires or when a fetch returns 401.

device  → your backend:  "give me the feed"
backend → Gondola:       GET /v1/swipe            (gk_ key + X-Gondola-User)
backend → Energixer:     POST /v1/playback-keys   (ek_ key)
                         { "viewer_ref": "<your stable user id>" }
                      →  { playback_key, viewer_ref, expires_at, video_ids }
backend → device:        { items: [...], playback_key, expires_at }
device:                  attaches Authorization: Bearer <playback_key>
                         to its Energixer fetches

Your backend, once per session:

const res = await fetch(`${ENERGIXER}/v1/playback-keys`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${ENERGIXER_API_KEY}`, // ek_ — backend only
    'Content-Type': 'application/json',
  },
  // Your STABLE user id, not a random per-session value — see below.
  body: JSON.stringify({ viewer_ref: user.id }),
});
const { playback_key, expires_at } = await res.json();
// Ship playback_key + expires_at down with the feed payload.

Web (hls.js) — the header goes on every manifest and segment request:

const hls = new Hls({
  xhrSetup: (xhr) =>
    xhr.setRequestHeader('Authorization', `Bearer ${playbackKey}`),
});
hls.loadSource(ENERGIXER + item.video.hls_url);
hls.attachMedia(videoEl);

React Native — react-native-video can send headers itself, so it needs none of the browser workarounds below:

<Video
  source={{
    uri: ENERGIXER + item.video.hls_url,
    headers: { Authorization: `Bearer ${playbackKey}` },
  }}
  controls
/>

Five things to get right

Posters (<img>) and caption tracks (<track>) have the same header problem in a browser; fetch them yourself and hand the element a blob URL. The playback guide has the full list.

Alternative: relay mode

If you prefer your backend in the loop, receive the webhook yourself and relay via Gondola's PATCH /v1/videos/:id with the URLs from the payload. Same data, one extra hop, your code owns the policy.

Checklist