Add an image
Save a banner or billboard in Spyne from a public link, so you can reuse it in many promotions by its id.
Adding the same link twice returns the image you already have instead of making a copy.
Before you call it
This step is optional: Create a promotion also accepts image links directly and saves them for you.
The link must be public and reachable. Spyne fetches it and refuses links to private or internal addresses (such as localhost or a private IP), links that don't resolve, and links that don't answer with a 2xx or 3xx status within 30 seconds. Use a direct link to a PNG or JPG file.
Counts toward the 30 changes a minute per rooftop.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
type | string | Required | banner (laid over a photo, such as a price strip) or billboard (a full image added as its own slide). |
url | string | Required | Public link to the image. Spaces at either end are trimmed. |
Sample request
curl --request POST \
--url 'https://api.spyne.ai/api/pv1/branding-assets' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"type": "banner",
"url": "https://example.com/fall-offer.png"
}'import requests
url = "https://api.spyne.ai/api/pv1/branding-assets"
headers = {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
}
payload = {"type": "banner", "url": "https://example.com/fall-offer.png"}
response = requests.post(url, headers=headers, json=payload)
print(response.status_code, response.json())const response = await fetch("https://api.spyne.ai/api/pv1/branding-assets", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ type: "banner", url: "https://example.com/fall-offer.png" }),
});
console.log(response.status, await response.json());Sample response
{
"success": true,
"statusCode": 201,
"message": "Asset created",
"data": {
"id": "1584",
"type": "banner",
"url": "https://example.com/fall-offer.png",
"duplicate": false
}
}{
"success": true,
"statusCode": 200,
"message": "An asset already exists for this exact URL on this team",
"data": {
"id": "1584",
"type": "banner",
"url": "https://example.com/fall-offer.png",
"duplicate": true,
"note": "If this URL now serves a different image than when it was first uploaded, or that asset was deleted, change the URL slightly (e.g. append ?v=2) to create a new asset. Otherwise, reuse this id."
}
}Response parameters (201, 200)
| Parameter | Type | Description |
|---|---|---|
data.id | string | The image id. Send it as assetId in a promotion. |
data.type | string | banner or billboard. |
data.url | string | The saved link. |
data.duplicate | boolean | false for a new image (201). true when this exact link was already saved with the same type (200); the existing image is returned. |
data.note | string | Only when duplicate is true. |
The same link saved under the other type (banner vs billboard) creates a new image. Changed the picture but kept the same link? Add something like ?v=2 to the link so it's saved as a new image.
Errors
| Status | Code | When it happens |
|---|---|---|
400 | INVALID_REQUEST | type or url is missing or invalid. details.errors lists every problem. |
400 | INVALID_ASSET_URL | The link is malformed, private or internal, doesn't resolve, or didn't answer within 30 seconds. |
400 | VALIDATION_FAILED | Spyne refused the image; message says why. |
400 | TEAM_NOT_RESOLVED | The API key isn't linked to a rooftop. |
401 | UNAUTHORIZED | The API key is missing or wrong. |
429 | RATE_LIMITED | More than 30 changes this minute. Wait the seconds in Retry-After. |
502 / 503 | SERVICE_UNAVAILABLE | A Spyne service is briefly unavailable. Retry shortly. |
Every code is explained in Errors and FAQs. Images can't be deleted through this API.
What happens next
Send the id as assetId when you check and create a promotion.
Updated 2 days ago
