Skip to content
Developers
Open app

Set an attribute

PUT
/v1/media/{id}/attributes/{attribute_id}
const url = 'https://api.focal.inc/v1/media/786beeda-55ac-4025-bf6e-43b2918e84a9/attributes/846196ad-9fa2-40a8-8938-655a962220e1';
const options = {
method: 'PUT',
headers: {
'Idempotency-Key': 'looker-sync-2027-03-04-0001',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: '{"value":["Spring Launch"]}'
};
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}

Set (or clear, with null) one custom-field value on a media — attribute_id is a media attribute definition id (see GET /v1/media-attributes). The write replaces the attribute’s whole value: for a multi-value attribute, pass the complete array. Values follow the attribute’s type — tags accepts option ids or names, user accepts user ids, date an ISO 8601 string. Returns the updated media (the new value appears under attributes). This writes the media’s own attribute value; board-row mediacolumn cells keep their own stored copy and are not updated — write through PATCH /v1/boards/{id}/rows/{rowId} to change a row’s cell (which also pushes the value to the row’s attached media). 404 when the media or the attribute definition doesn’t exist, isn’t accessible, or the attribute isn’t in the media’s space.

Required scope: media:write. Rate-limit cost: 10. Honors an optional Idempotency-Key.

id
required
string format: uuid

Example

786beeda-55ac-4025-bf6e-43b2918e84a9
attribute_id
required
string format: uuid

Example

846196ad-9fa2-40a8-8938-655a962220e1
Idempotency-Key
string
<= 255 characters

Example

looker-sync-2027-03-04-0001

Client-generated key (≤255 chars) for safe retries. A retry carrying the same key replays the original response instead of re-applying the write; reusing a key with a different body returns 400, and a still-in-flight key returns 409. Keys are remembered for 24 hours. A UUID v4 is recommended — generate one per logical write and reuse it only for retries of that write.

Media typeapplication/json
object
value
required
Any of:
Array

OK

Media typeapplication/json
object
data
required
object
id
required
string format: uuid
name
required
string
type
required
string
Allowed values: video image playable file
format
required

File extension.

string
media_type
required

IANA media type (the spec’s current name for what was historically called MIME type) derived from format. Provided as a convenience so clients don’t need to maintain their own extension → media-type table when uploading to ad platforms.

Don’t confuse this with the top-level type field — type is Focal’s coarse asset bucket (image/video/playable/file), while media_type is the precise IANA identifier ad platforms expect (e.g. image/jpeg, video/mp4).

string
organization_id
required
string format: uuid
space_id
required
string format: uuid
folder
required

Parent folder summary, or null if the media is at the space root.

object
id
required
string format: uuid
name
required
string
size
required

File size in bytes.

integer
width
required

Pixel width for visual media. null when dimensions don’t apply (e.g. type=file) or couldn’t be extracted.

integer
nullable
height
required

Pixel height for visual media. null when dimensions don’t apply (e.g. type=file) or couldn’t be extracted.

integer
nullable
aspect_ratio
required

width / height for visual media. null when dimensions don’t apply (e.g. type=file) or couldn’t be extracted.

number
nullable
duration
required

Duration in seconds for video files. null for other types.

number
nullable
page_count
required

Total page count for multi-page formats (e.g. PDF). null for other types.

integer
nullable
description
required
string
nullable
summary
required

AI-generated summary of the media content, produced by Focal’s analysis pipeline. null until analysis has run (or for media types that aren’t analyzed). Read-only — for the customer-managed free-text field use description.

string
nullable
status
required

The media’s workflow status name (e.g. Approved), one of the space’s configured media statuses — see the Space resource’s media_statuses for the catalog. null when no status is set. Same read/write semantics as a tags attribute: reads return the name; writes accept the status id or name.

string
nullable
creative_id
required

Generated identifier produced by a Creative ID naming convention — the combined <concept_id>-<variant_id> string the customer configured. null when the media isn’t governed by a Creative ID configuration.

string
nullable
concept_id
required

Concept portion of the Creative ID — the stable text/date/counter prefix shared across variants. null when the media has no Creative ID.

string
nullable
variant_id
required

Variant number within a concept. Incremented per variant of the same concept. null when the media has no Creative ID.

integer
nullable
attributes
required

Custom-field values attached to this media — one entry per populated field. A multi-value field carries an array value; a single-value field a scalar. Empty array when no custom fields are populated.

Array<object>
object
attribute_id
required
string format: uuid
attribute_name
required
string
attribute_type
required

The kind of custom field this value belongs to. Combined with multiple, tells you the JSON type of value (for a multi-value field it’s the type of each array element):

  • number → JSON number
  • checkbox → JSON true / false
  • date → ISO 8601 string
  • tags → option id string (option names in display_value)
  • user → user id string (display names in display_value)
  • everything else → JSON string (e.g. URLs for link)
string
Allowed values: text number tags checkbox date link user
multiple
required

Whether this field holds multiple values. When true, value is an array (one element per stored value); when false, value is a single scalar.

boolean
value
required
Any of:
Any of:
string
display_value
required
Any of:
string
exports
required

External identifiers for the ad platforms this media has been successfully exported to (YouTube, Meta, TikTok). One entry per export — re-exports and separate destinations each add an entry, so a media can appear more than once for the same channel. Empty array when the media hasn’t been exported, or only to channels without creative identifiers (e.g. Google Drive). In-progress and failed exports are excluded.

Array
Any of:
object
kind
required
string
Allowed values: youtube
type
required
string
Allowed values: video
video_id
required

YouTube video id — the watch id.

string
url
required

Canonical YouTube watch URL.

string
youtube_channel
required

Id of the YouTube channel the video was uploaded to (e.g. UC…).

string
exported_at
required

UTC ISO timestamp of when this export was created.

string format: date-time
metrics
required

Ad-platform performance for this media, rolled up by Focal’s metrics sync across every ad it has run in — where it ran (ad_accounts / campaigns / ad_sets), when it was last live (last_activity), and its lifetime totals (insights). null for media that has never run as an ad, or when the organization has no ad accounts connected. Use the metrics request parameter to choose which metrics come back.

object
last_activity
required

The most recent date the ad platforms reported any data for this media — i.e. when it was last live. null when no dated data has been synced.

string format: date-time
nullable
ad_accounts
required

Ad accounts this media has been running in.

Array<object>
object
id
required

Focal’s id for this ad account / campaign / ad set.

string
name
required

Its name as reported by the ad platform. null when the sync hasn’t recorded one.

string
nullable
campaigns
required

Campaigns this media has been running in.

Array<object>
object
id
required

Focal’s id for this ad account / campaign / ad set.

string
name
required

Its name as reported by the ad platform. null when the sync hasn’t recorded one.

string
nullable
ad_sets
required

Ad sets this media has been running in.

Array<object>
object
id
required

Focal’s id for this ad account / campaign / ad set.

string
name
required

Its name as reported by the ad platform. null when the sync hasn’t recorded one.

string
nullable
insights
required

Lifetime performance totals, one entry per metric. Which metrics appear is controlled by the metrics request parameter — by default the core set, and only those a media actually has data for. Core metrics come first, then the rest alphabetically.

Array<object>
object
metric
required

The metric key — a core name (spend, impressions, clicks, reach, installs, watch_min_3_seconds) or a platform-specific one (e.g. meta.omni_purchase). Pass these to the metrics parameter to select them.

string
value
required

The metric’s total across every ad this media has run in, over all time — not a windowed figure. Currency amounts are in the ad account’s own currency.

number
created_at
required
string format: date-time
updated_at
required
string format: date-time
html_url
required

Browser URL that opens this media in the Focal web app (its space’s library). Hand this to users as a clickable link to the exact asset.

string

Example

{
"data": {
"id": "786beeda-55ac-4025-bf6e-43b2918e84a9",
"name": "spring-hero-4x5.mp4",
"type": "video",
"format": "mp4",
"media_type": "video/mp4",
"organization_id": "b46d9033-24b2-4441-87ad-2151088fde01",
"space_id": "6916bc3d-e1ab-425b-ac25-eefada01a9a2",
"folder": {
"id": "80539d38-3a8e-48f8-929b-8c0e9a22ab43",
"name": "Spring 2027"
},
"size": 48213504,
"width": 1080,
"height": 1350,
"aspect_ratio": 0.8,
"duration": 18.5,
"page_count": null,
"description": "Hero cut for Meta and TikTok placements, 4:5.",
"summary": "A trail runner crests a ridge at sunrise in Alder & Pine gear, then the logo and Spring 2027 title card appear.",
"status": "Approved",
"creative_id": "SPR27-0042-3",
"concept_id": "SPR27-0042",
"variant_id": 3,
"attributes": [
{
"attribute_id": "0f35c2a2-a71a-4423-b1f2-3b5d795c6332",
"attribute_name": "Usage rights",
"attribute_type": "text",
"multiple": false,
"value": "Paid social only, 12 months",
"display_value": "Paid social only, 12 months"
}
],
"exports": [
{
"kind": "youtube",
"type": "video",
"video_id": "u8Lk2VnTq4c",
"url": "https://www.youtube.com/watch?v=u8Lk2VnTq4c",
"youtube_channel": "UCa1Dp9Qw3nXr7LbT5vHkZ2g",
"exported_at": "2027-03-05T09:30:00Z"
}
],
"metrics": {
"last_activity": "2027-03-14T00:00:00Z",
"ad_accounts": [
{
"id": "act_2048117733",
"name": "Alder & Pine – Paid Social"
}
],
"campaigns": [
{
"id": "120211482993410331",
"name": "Spring 2027 – Prospecting"
}
],
"ad_sets": [
{
"id": "120211482993520447",
"name": "Trail runners 25–44"
}
],
"insights": [
{
"metric": "spend",
"value": 1842.5
},
{
"metric": "impressions",
"value": 412930
},
{
"metric": "clicks",
"value": 9318
}
]
},
"created_at": "2027-03-04T10:15:00Z",
"updated_at": "2027-03-06T14:42:00Z",
"html_url": "https://app.focal.inc/spaces/6916bc3d-e1ab-425b-ac25-eefada01a9a2/library?file=786beeda-55ac-4025-bf6e-43b2918e84a9&folder=80539d38-3a8e-48f8-929b-8c0e9a22ab43"
}
}

The request was malformed (invalid cursor, unknown type, validation failure, etc.). error.code is bad_request.

Media typeapplication/json

Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.

object
error
required
object
code
required
string
Allowed values: unauthenticated forbidden not_found bad_request conflict rate_limited internal_error
message
required
string
details
Array<object>
object
field
string
issue
required
string

Example

{
"error": {
"code": "bad_request",
"message": "Invalid request body",
"details": [
{
"field": "space_id",
"issue": "Invalid UUID"
}
]
}
}

Missing, invalid, or disabled token. error.code is unauthenticated.

Media typeapplication/json

Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.

object
error
required
object
code
required
string
Allowed values: unauthenticated forbidden not_found bad_request conflict rate_limited internal_error
message
required
string
details
Array<object>
object
field
string
issue
required
string

Example

{
"error": {
"code": "unauthenticated",
"message": "Bearer token is missing or invalid"
}
}

The token doesn’t have the scope this endpoint requires. error.code is forbidden.

Media typeapplication/json

Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.

object
error
required
object
code
required
string
Allowed values: unauthenticated forbidden not_found bad_request conflict rate_limited internal_error
message
required
string
details
Array<object>
object
field
string
issue
required
string

Example

{
"error": {
"code": "forbidden",
"message": "Token doesn't have the required scope"
}
}

Resource doesn’t exist or is outside the token’s space allow-list. The two cases share a response shape so a restricted token can’t probe for the existence of out-of-scope spaces. error.code is not_found.

Media typeapplication/json

Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.

object
error
required
object
code
required
string
Allowed values: unauthenticated forbidden not_found bad_request conflict rate_limited internal_error
message
required
string
details
Array<object>
object
field
string
issue
required
string

Example

{
"error": {
"code": "not_found",
"message": "Not found"
}
}

The request conflicts with the current state. Most commonly a concurrent request carrying the same Idempotency-Key is still in flight. error.code is conflict.

Media typeapplication/json

Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.

object
error
required
object
code
required
string
Allowed values: unauthenticated forbidden not_found bad_request conflict rate_limited internal_error
message
required
string
details
Array<object>
object
field
string
issue
required
string

Example

{
"error": {
"code": "conflict",
"message": "A request with this Idempotency-Key is already being processed"
}
}

Rate limit exceeded. error.code is rate_limited. Wait the number of seconds in the Retry-After header before retrying. Quotas are per-token; see the “Rate limiting” section at the top of this document.

Media typeapplication/json

Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.

object
error
required
object
code
required
string
Allowed values: unauthenticated forbidden not_found bad_request conflict rate_limited internal_error
message
required
string
details
Array<object>
object
field
string
issue
required
string

Example

{
"error": {
"code": "rate_limited",
"message": "Rate limit of 1000 units per 60s exceeded (this request cost 5)"
}
}
Retry-After
integer
Example
12

Seconds to wait before retrying.