Wrap a creative
Turn a creative's URL into a Vojo Passport you can schedule.
You need the wrappers:write scope and a screen. If you're not sure what a Passport does, read Passport and wrapping.
Send the creative's URL
curl -X POST https://api.vojo-media.com/v1/wrappers \
-H "Authorization: Bearer $VOJO_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"content_source": "https://cdn.your-cms.example/creatives/77.mp4",
"content_type": "video",
"screen_id": "'"$SCREEN_ID"'",
"external_id": "creative-77",
"source": "YOUR_CMS",
"duration": 15,
"fingerprint": true,
"delivery_mode": "archive",
"metadata": { "line_item": "LI-2291" }
}'const wrapper = await vojo.wrappers.create({
content_source: "https://cdn.your-cms.example/creatives/77.mp4",
content_type: "video",
screen_id: screenId,
external_id: "creative-77",
source: "YOUR_CMS",
duration: 15,
fingerprint: true,
delivery_mode: "archive",
metadata: { line_item: "LI-2291" },
});wrapper = client.wrappers.create(
content_source="https://cdn.your-cms.example/creatives/77.mp4",
content_type="video",
screen_id=screen_id,
external_id="creative-77",
source="YOUR_CMS",
duration=15,
fingerprint=True,
delivery_mode="archive",
metadata={"line_item": "LI-2291"},
)wrapper, err := client.Wrappers.Create(ctx, &vojo.CreateWrapperRequest{
ContentSource: vojo.Ptr("https://cdn.your-cms.example/creatives/77.mp4"),
ContentType: vojo.Ptr(vojo.AdvertisementTypeVideo),
ScreenID: vojo.Ptr(screenID),
ExternalID: vojo.Ptr("creative-77"),
Source: vojo.Ptr("YOUR_CMS"),
Duration: vojo.Ptr(15.0),
Fingerprint: vojo.Ptr(true),
DeliveryMode: vojo.Ptr(vojo.WrapperDeliveryModeArchive),
Metadata: vojo.Value(map[string]any{"line_item": "LI-2291"}),
})Wrapper wrapper = client.wrappers().create(CreateWrapperRequest.builder()
.contentSource("https://cdn.your-cms.example/creatives/77.mp4")
.contentType("video")
.screenId(screenId)
.externalId("creative-77")
.source("YOUR_CMS")
.duration(15.0)
.fingerprint(true)
.deliveryMode("archive")
.metadata(Map.of("line_item", "LI-2291"))
.build());| Field | What to send |
|---|---|
content_source |
A public http(s) URL to the creative. Vojo downloads it, so you don't upload the file first. |
content_type |
video, image or html. |
screen_id |
The screen this wrapper will play on. Send it every time. |
external_id, source |
Your own creative id, and your namespace for it. You can filter plays by them later. |
fingerprint |
true to fingerprint the creative. This needs external_id, or advertisement_id if you're wrapping an existing advertisement. Send duration too, unless the advertisement already has a length. |
delivery_mode |
How you want the Passport back. See below. |
metadata |
Anything you want back later. Vojo returns it unchanged and never scores it. |
Without
screen_id, the wrapper's plays have no screen, so a Harvester can never corroborate them. The response then carriesmeta.warnings[0].code = "screen_id_missing". Make one wrapper per creative per screen.
Choose a delivery mode
inlineis the default, and returns the Passport HTML indata.html. You get it only in this response:GET /v1/wrappers/{wrapper_id}returnshtmlasnull, and so does an idempotent replay.archivestores a zip and returnsdata.url. Choose this if you might need the file again.
Handle both outcomes
Today a wrap always completes within the request and returns 201 with status: "ready". The contract also allows 202 with status: "processing", so build for it now. When you get 202, poll GET /v1/wrappers/{wrapper_id}, honouring Retry-After, until the status is ready or failed.
If Vojo can't fetch the creative, the response is 422 with errors[0].code set to creative_fetch_failed or creative_not_found, and the pointer /content_source.
Wrapping is limited to 30 requests a minute per key.
Play it unmodified
Schedule the Passport (the inline HTML, or the archive's index.html) in place of the raw creative, for the creative's full slot. Don't edit, re-encode, crop or overlay it.
Next: receive webhooks as your plays come in.