Relate a record to a story
Upserts a relation for the record and role. The record must belong to this organization and be readable by the user. 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}/relations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"record_type": "program",
"record_id": 1,
"relationship_role": "Relationship Role",
"label_snapshot": "Label Snapshot",
"metadata_json": {}
}
'import requests
url = "https://api.gratona.com/v1/admin/impact-stories/{storyId}/relations"
payload = {
"record_type": "program",
"record_id": 1,
"relationship_role": "Relationship Role",
"label_snapshot": "Label Snapshot",
"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({
record_type: 'program',
record_id: 1,
relationship_role: 'Relationship Role',
label_snapshot: 'Label Snapshot',
metadata_json: {}
})
};
fetch('https://api.gratona.com/v1/admin/impact-stories/{storyId}/relations', 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}/relations",
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([
'record_type' => 'program',
'record_id' => 1,
'relationship_role' => 'Relationship Role',
'label_snapshot' => 'Label Snapshot',
'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}/relations"
payload := strings.NewReader("{\n \"record_type\": \"program\",\n \"record_id\": 1,\n \"relationship_role\": \"Relationship Role\",\n \"label_snapshot\": \"Label Snapshot\",\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}/relations")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"record_type\": \"program\",\n \"record_id\": 1,\n \"relationship_role\": \"Relationship Role\",\n \"label_snapshot\": \"Label Snapshot\",\n \"metadata_json\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gratona.com/v1/admin/impact-stories/{storyId}/relations")
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 \"record_type\": \"program\",\n \"record_id\": 1,\n \"relationship_role\": \"Relationship Role\",\n \"label_snapshot\": \"Label Snapshot\",\n \"metadata_json\": {}\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": 42,
"client_id": 42,
"impact_story_id": 42,
"record_id": 42,
"record_type": "Education update",
"relationship_role": "Education update",
"label_snapshot": "Education update",
"metadata_json": {},
"created_at": "2026-10-20T12:00:00Z",
"updated_at": "2026-10-20T12:00:00Z"
}
}{
"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.
Workspace record type recognized by the record registry, such as program, entity or donor. The selected record must be readable.
64"program"
Record id. Validation: required, integer.
1
Relationship role. Validation: nullable, string, max:64.
64"Relationship Role"
Label snapshot. Validation: nullable, string, max:255.
255"Label Snapshot"
Metadata json. Validation: nullable, array.
Show child attributes
Show child attributes
Response
Operation result.
Show child attributes
Show child attributes
curl --request POST \
--url https://api.gratona.com/v1/admin/impact-stories/{storyId}/relations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"record_type": "program",
"record_id": 1,
"relationship_role": "Relationship Role",
"label_snapshot": "Label Snapshot",
"metadata_json": {}
}
'import requests
url = "https://api.gratona.com/v1/admin/impact-stories/{storyId}/relations"
payload = {
"record_type": "program",
"record_id": 1,
"relationship_role": "Relationship Role",
"label_snapshot": "Label Snapshot",
"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({
record_type: 'program',
record_id: 1,
relationship_role: 'Relationship Role',
label_snapshot: 'Label Snapshot',
metadata_json: {}
})
};
fetch('https://api.gratona.com/v1/admin/impact-stories/{storyId}/relations', 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}/relations",
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([
'record_type' => 'program',
'record_id' => 1,
'relationship_role' => 'Relationship Role',
'label_snapshot' => 'Label Snapshot',
'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}/relations"
payload := strings.NewReader("{\n \"record_type\": \"program\",\n \"record_id\": 1,\n \"relationship_role\": \"Relationship Role\",\n \"label_snapshot\": \"Label Snapshot\",\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}/relations")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"record_type\": \"program\",\n \"record_id\": 1,\n \"relationship_role\": \"Relationship Role\",\n \"label_snapshot\": \"Label Snapshot\",\n \"metadata_json\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gratona.com/v1/admin/impact-stories/{storyId}/relations")
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 \"record_type\": \"program\",\n \"record_id\": 1,\n \"relationship_role\": \"Relationship Role\",\n \"label_snapshot\": \"Label Snapshot\",\n \"metadata_json\": {}\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": 42,
"client_id": 42,
"impact_story_id": 42,
"record_id": 42,
"record_type": "Education update",
"relationship_role": "Education update",
"label_snapshot": "Education update",
"metadata_json": {},
"created_at": "2026-10-20T12:00:00Z",
"updated_at": "2026-10-20T12:00:00Z"
}
}{
"message": "Unauthorized",
"status": "error"
}{
"message": "Not Found",
"status": "error"
}{
"errors": {},
"status": "error",
"message": "The given data was invalid."
}