Outros — a branded tail card on your share artifact

An outro is a short clip appended to the end of a video's web.mp4. That is the progressive MP4 a social crawler plays in an og:video card, so an outro is what puts your logo on the thing people see in a link preview.

It goes on that file and nothing else. HLS and DASH are packaged exactly as they are today, so a viewer who presses play in your player sees the video unchanged. If you want a tail card everyone watches, that is a different feature and this is not it.

The shape of it

Two ideas, and everything else follows from them:

That split is why replacing your outro is safe: a video that already resolved the old clip keeps it, and the old clip keeps serving new uploads until the replacement finishes encoding. Rotating an outro never makes its slug unusable.

Uploading one

Same tus flow as a video (see the upload guide) — only the initiate call differs.

curl -X POST "$ENERGIXER/v1/outros" \
  -H "Authorization: Bearer $ENERGIXER_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"slug":"brand","content_type":"video/mp4","file_size":184320,"label":"Brand card"}'
{
  "outro_id": "9f2c…",
  "slug": "brand",
  "tus_upload_url": "/v1/videos/upload/9f2c…",
  "upload_token": "…"
}

Then upload the bytes to tus_upload_url with the upload_token, exactly as you would a video — the video_id tus metadata is the outro_id. Poll until it is ready:

curl "$ENERGIXER/v1/outros" -H "Authorization: Bearer $ENERGIXER_KEY"
{ "outros": [
  { "outro_id": "9f2c…", "serving_outro_id": "9f2c…", "slug": "brand",
    "label": "Brand card", "state": "available",
    "duration_seconds": 4, "width": 1920, "height": 1080, "…": "…" }
] }

serving_outro_id is what an upload naming this slug would get right now. It differs from outro_id only while a replacement is encoding — then state describes the incoming clip and serving_outro_id names the one still doing the work.

Limits. 30 seconds per outro, 50 slugs per domain, and the bytes count against your normal upload and storage quotas. A clip over the ceiling is rejected at initiate with duration_too_long.

Using one

Per upload:

POST /v1/videos
{ "content_type": "video/mp4", "file_size": 8123456, "outro_slug": "brand" }

Or for every upload, once:

PATCH /v1/domain/config
{ "default_outro_slug": "brand" }

With a domain default set, outro_slug still wins per video — including "outro_slug": null, which is how you opt one video out. Omitting the field means "use the domain default", so null is not the same as leaving it out.

The slug is resolved to an asset id at initiate and pinned to that video. Changing your outro afterwards does not change a video that is already queued.

Errors you can hit here

Code Meaning
outro_not_found (400) no outro of yours has that slug
outro_not_ready (409) the slug exists, but its clip has never finished encoding — poll and retry

Both bodies carry outro_slug and from. from: "domain_default" means your domain setting is what is broken, not the request you just made — worth checking before you go looking at your own code.

What comes out

On a finished video:

"outputs": {
  "web_mp4_url": "/videos/<id>/web.mp4",
  "web_mp4_width": 1280, "web_mp4_height": 720,
  "web_mp4_duration_seconds": 64,      // the video plus the tail card
  "web_mp4_outro_id": "9f2c…"          // which outro is actually in there
}

Three things to read carefully:

Things that will not happen

Removing one

curl -X DELETE "$ENERGIXER/v1/outros/brand" -H "Authorization: Bearer $ENERGIXER_KEY"

This frees the slug immediately — new uploads naming it get outro_not_found. Videos that already resolved it are not failed; if the bytes are gone by the time one encodes, it simply ships without the tail card.

In the console

The outros section of your domain does all of the above: upload, replace, delete, and set the domain default from a list of what actually exists.