Creatives are uploaded to a campaign using POST /campaigns/{campaignId}/creatives. The API supports four upload methods depending on your content type.
All upload methods require these headers:
| Header | Description |
|---|
X-API-Key | Your API key |
Accept | application/json |
Content-Type | Varies by method (see below) |
X-Filename | Optional. Sets the display name in the Advalidation UI. Defaults to a timestamp-based name if omitted. |
Content-Type: application/json
Best for: URLs, HTML tags, VAST tags, and other text-based content.
Send the content in a payload field:
curl -X POST https://app.advalidation.io/v2/campaigns/CAMPAIGN_ID/creatives \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key-here" \
-H "X-Filename: Interactive.html" \
-d '{"payload": "<iframe src=\"https://labs.advalidation.net/animation/10s/\" style=\"border:0; width:300px; height:250px;\" scrolling=\"no\"></iframe>"}'
const response = await fetch(
`${BASE_URL}/campaigns/${campaignId}/creatives`,
{
method: "POST",
headers: {
...headers,
"Content-Type": "application/json",
"X-Filename": "Interactive.html",
},
body: JSON.stringify({
payload:
'<iframe src="https://labs.advalidation.net/animation/10s/" style="border:0; width:300px; height:250px;" scrolling="no"></iframe>',
}),
}
);
const { data } = await response.json();
const creativeId = data[0].id;
Content-Type: application/json
Best for: Binary files (images, videos, ZIP archives) when you need to use JSON.
Encode the file as a base64 string and send it in the payload field:
BASE64=$(base64 < ./creative.mov)
curl -X POST https://app.advalidation.io/v2/campaigns/CAMPAIGN_ID/creatives \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key-here" \
-H "X-Filename: creative.mov" \
-d "{\"payload\": \"$BASE64\"}"
import { readFile } from "node:fs/promises";
const file = await readFile("./creative.mov");
const base64 = file.toString("base64");
const response = await fetch(
`${BASE_URL}/campaigns/${campaignId}/creatives`,
{
method: "POST",
headers: {
...headers,
"Content-Type": "application/json",
"X-Filename": "creative.mov",
},
body: JSON.stringify({ payload: base64 }),
}
);
Content-Type: text/plain
Best for: Raw HTML files or plain text content.
Send the file content directly as the request body:
curl -X POST https://app.advalidation.io/v2/campaigns/CAMPAIGN_ID/creatives \
-H "Accept: application/json" \
-H "Content-Type: text/plain" \
-H "X-API-Key: your-api-key-here" \
-H "X-Filename: creative.html" \
--data-binary @./creative.html
import { readFile } from "node:fs/promises";
const content = await readFile("./creative.html", "utf8");
const response = await fetch(
`${BASE_URL}/campaigns/${campaignId}/creatives`,
{
method: "POST",
headers: {
...headers,
"Content-Type": "text/plain",
"X-Filename": "creative.html",
},
body: content,
}
);
Content-Type: application/octet-stream
Best for: ZIP archives, images, videos, and other binary files. Most efficient for large files since there is no encoding overhead.
Send the raw file bytes as the request body:
curl -X POST https://app.advalidation.io/v2/campaigns/CAMPAIGN_ID/creatives \
-H "Accept: application/json" \
-H "Content-Type: application/octet-stream" \
-H "X-API-Key: your-api-key-here" \
-H "X-Filename: creative.zip" \
--data-binary @./creative.zip
import { readFile } from "node:fs/promises";
const file = await readFile("./creative.zip");
const response = await fetch(
`${BASE_URL}/campaigns/${campaignId}/creatives`,
{
method: "POST",
headers: {
...headers,
"Content-Type": "application/octet-stream",
"X-Filename": "creative.zip",
},
body: file,
}
);
| Method | Content-Type | Encoding overhead | Best for |
|---|
| JSON payload | application/json | None (text) | URLs, HTML tags, VAST tags |
| JSON base64 | application/json | ~30% larger | Binary files when JSON is required |
| Plain text | text/plain | None | Raw HTML files |
| Binary | application/octet-stream | None | ZIP, images, videos (most efficient) |