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:
- A slug is a name you choose (
brand,autumn-campaign). It is what you pass on an upload, and it is a pointer you can move. - An asset is one uploaded clip, with an id. Re-uploading a slug mints a new asset and repoints the slug; it never rewrites the old one.
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:
duration_secondsat the top level is still the source's. With an outro appended these are different files and the numbers legitimately disagree — useweb_mp4_duration_secondswhen you are describingweb.mp4.- The tail card is fitted into the video's frame, letterboxed if the shapes differ. A 16:9 card on a portrait video gets bars, not a stretch.
web_mp4_outro_idis reported only when the outro really made it in. If it isnullon a video you attached one to, the outro was unavailable at encode time and the video shipped without it — see below.
Things that will not happen
- An outro will never fail your video. If the clip was deleted, or its bytes
could not be fetched, or the composition failed, the video ships with a plain
web.mp4andweb_mp4_outro_id: null. A tail card is not worth a lost upload. - Nobody can watch your outro. It never appears in
GET /v1/videos, every/v1/videos/:idroute 404s for one, and asking the delivery plane for an outro id gets you the same answer as asking for an id that was never issued — with or without a playback key. There is no URL that plays one. - HLS is never touched. If you are testing this, check the media playlists: their total runtime is the source's, always.
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.