Attach a source or upload to a story
Attaches a tenant-owned upload or source link. Library authorization markers cannot be supplied by the caller; approved stories are requeued when their media changes. Requires the impact_stories feature. Requires permission: one of impact_stories_manage.
curl --request POST \
--url https://api.gratona.com/v1/admin/impact-stories/{storyId}/assets \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"upload_id": 1,
"asset_type": "Asset Type",
"title": "Field update from the education program",
"caption": "Caption",
"source_url": "https://example.org/story",
"drive_file_id": "Drive File Id",
"consent_status": "unknown",
"is_primary": true,
"metadata_json": {}
}
'import requests
url = "https://api.gratona.com/v1/admin/impact-stories/{storyId}/assets"
payload = {
"upload_id": 1,
"asset_type": "Asset Type",
"title": "Field update from the education program",
"caption": "Caption",
"source_url": "https://example.org/story",
"drive_file_id": "Drive File Id",
"consent_status": "unknown",
"is_primary": True,
"metadata_json": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
upload_id: 1,
asset_type: 'Asset Type',
title: 'Field update from the education program',
caption: 'Caption',
source_url: 'https://example.org/story',
drive_file_id: 'Drive File Id',
consent_status: 'unknown',
is_primary: true,
metadata_json: {}
})
};
fetch('https://api.gratona.com/v1/admin/impact-stories/{storyId}/assets', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.gratona.com/v1/admin/impact-stories/{storyId}/assets",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'upload_id' => 1,
'asset_type' => 'Asset Type',
'title' => 'Field update from the education program',
'caption' => 'Caption',
'source_url' => 'https://example.org/story',
'drive_file_id' => 'Drive File Id',
'consent_status' => 'unknown',
'is_primary' => true,
'metadata_json' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.gratona.com/v1/admin/impact-stories/{storyId}/assets"
payload := strings.NewReader("{\n \"upload_id\": 1,\n \"asset_type\": \"Asset Type\",\n \"title\": \"Field update from the education program\",\n \"caption\": \"Caption\",\n \"source_url\": \"https://example.org/story\",\n \"drive_file_id\": \"Drive File Id\",\n \"consent_status\": \"unknown\",\n \"is_primary\": true,\n \"metadata_json\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.gratona.com/v1/admin/impact-stories/{storyId}/assets")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"upload_id\": 1,\n \"asset_type\": \"Asset Type\",\n \"title\": \"Field update from the education program\",\n \"caption\": \"Caption\",\n \"source_url\": \"https://example.org/story\",\n \"drive_file_id\": \"Drive File Id\",\n \"consent_status\": \"unknown\",\n \"is_primary\": true,\n \"metadata_json\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gratona.com/v1/admin/impact-stories/{storyId}/assets")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"upload_id\": 1,\n \"asset_type\": \"Asset Type\",\n \"title\": \"Field update from the education program\",\n \"caption\": \"Caption\",\n \"source_url\": \"https://example.org/story\",\n \"drive_file_id\": \"Drive File Id\",\n \"consent_status\": \"unknown\",\n \"is_primary\": true,\n \"metadata_json\": {}\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": 42,
"client_id": 42,
"impact_story_id": 42,
"upload_id": 42,
"asset_type": "Education update",
"title": "Education update",
"caption": "Education update",
"source_url": "Education update",
"drive_file_id": "Education update",
"consent_status": "unknown",
"is_primary": true,
"metadata_json": {},
"created_by_user_id": 42,
"created_at": "2026-10-20T12:00:00Z",
"updated_at": "2026-10-20T12:00:00Z",
"preview": {
"id": 42,
"asset_type": "Education update",
"title": "Education update",
"caption": "Education update",
"provider": "Education update",
"available": true,
"open_source": true,
"workspace_file_id": "Education update",
"preview_url": "Education update",
"thumbnail_url": "Education update",
"source_url": "Education update",
"download_url": "Education update",
"drive_file_id": "Education update",
"consent_status": "unknown",
"is_primary": true,
"metadata_json": {}
}
}
}{
"message": "Unauthorized",
"status": "error"
}{
"message": "Not Found",
"status": "error"
}{
"errors": {},
"status": "error",
"message": "The given data was invalid."
}Authorizations
Passport bearer token authentication for legacy admin sessions.
Path Parameters
Story id in the current organization.
Body
Validated request fields for this operation.
Upload id. Validation: nullable, integer.
1
Asset type. Validation: nullable, string, max:32.
32"Asset Type"
Title. Validation: nullable, string, max:255.
255"Field update from the education program"
Caption. Validation: nullable, string, max:2000.
2000"Caption"
Source url. Validation: nullable, url, max:2048.
2048"https://example.org/story"
Drive file id. Validation: nullable, string, max:256.
256"Drive File Id"
Consent status. Validation: nullable, string; allowed values: unknown, internal_only, donor_safe, public_safe.
unknown, internal_only, donor_safe, public_safe, null "unknown"
Is primary. Validation: nullable, boolean.
true
Metadata json. Validation: nullable, array.
Show child attributes
Show child attributes
Response
Operation result.
Story asset or Library reference with current preview. Library references can return the narrower media preview shape.
Show child attributes
Show child attributes
curl --request POST \
--url https://api.gratona.com/v1/admin/impact-stories/{storyId}/assets \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"upload_id": 1,
"asset_type": "Asset Type",
"title": "Field update from the education program",
"caption": "Caption",
"source_url": "https://example.org/story",
"drive_file_id": "Drive File Id",
"consent_status": "unknown",
"is_primary": true,
"metadata_json": {}
}
'import requests
url = "https://api.gratona.com/v1/admin/impact-stories/{storyId}/assets"
payload = {
"upload_id": 1,
"asset_type": "Asset Type",
"title": "Field update from the education program",
"caption": "Caption",
"source_url": "https://example.org/story",
"drive_file_id": "Drive File Id",
"consent_status": "unknown",
"is_primary": True,
"metadata_json": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
upload_id: 1,
asset_type: 'Asset Type',
title: 'Field update from the education program',
caption: 'Caption',
source_url: 'https://example.org/story',
drive_file_id: 'Drive File Id',
consent_status: 'unknown',
is_primary: true,
metadata_json: {}
})
};
fetch('https://api.gratona.com/v1/admin/impact-stories/{storyId}/assets', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.gratona.com/v1/admin/impact-stories/{storyId}/assets",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'upload_id' => 1,
'asset_type' => 'Asset Type',
'title' => 'Field update from the education program',
'caption' => 'Caption',
'source_url' => 'https://example.org/story',
'drive_file_id' => 'Drive File Id',
'consent_status' => 'unknown',
'is_primary' => true,
'metadata_json' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.gratona.com/v1/admin/impact-stories/{storyId}/assets"
payload := strings.NewReader("{\n \"upload_id\": 1,\n \"asset_type\": \"Asset Type\",\n \"title\": \"Field update from the education program\",\n \"caption\": \"Caption\",\n \"source_url\": \"https://example.org/story\",\n \"drive_file_id\": \"Drive File Id\",\n \"consent_status\": \"unknown\",\n \"is_primary\": true,\n \"metadata_json\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.gratona.com/v1/admin/impact-stories/{storyId}/assets")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"upload_id\": 1,\n \"asset_type\": \"Asset Type\",\n \"title\": \"Field update from the education program\",\n \"caption\": \"Caption\",\n \"source_url\": \"https://example.org/story\",\n \"drive_file_id\": \"Drive File Id\",\n \"consent_status\": \"unknown\",\n \"is_primary\": true,\n \"metadata_json\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gratona.com/v1/admin/impact-stories/{storyId}/assets")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"upload_id\": 1,\n \"asset_type\": \"Asset Type\",\n \"title\": \"Field update from the education program\",\n \"caption\": \"Caption\",\n \"source_url\": \"https://example.org/story\",\n \"drive_file_id\": \"Drive File Id\",\n \"consent_status\": \"unknown\",\n \"is_primary\": true,\n \"metadata_json\": {}\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": 42,
"client_id": 42,
"impact_story_id": 42,
"upload_id": 42,
"asset_type": "Education update",
"title": "Education update",
"caption": "Education update",
"source_url": "Education update",
"drive_file_id": "Education update",
"consent_status": "unknown",
"is_primary": true,
"metadata_json": {},
"created_by_user_id": 42,
"created_at": "2026-10-20T12:00:00Z",
"updated_at": "2026-10-20T12:00:00Z",
"preview": {
"id": 42,
"asset_type": "Education update",
"title": "Education update",
"caption": "Education update",
"provider": "Education update",
"available": true,
"open_source": true,
"workspace_file_id": "Education update",
"preview_url": "Education update",
"thumbnail_url": "Education update",
"source_url": "Education update",
"download_url": "Education update",
"drive_file_id": "Education update",
"consent_status": "unknown",
"is_primary": true,
"metadata_json": {}
}
}
}{
"message": "Unauthorized",
"status": "error"
}{
"message": "Not Found",
"status": "error"
}{
"errors": {},
"status": "error",
"message": "The given data was invalid."
}