Update stewardship-plan strategy
Uses the plan version. Completed or archived plans cannot be edited. Active/paused plans retain an eligible action contact; managed plans require tier, goal and contact. Requires stewardship_major_donor_plans_v1. Relationship-group subjects also require stewardship_relationship_groups_v1. Subject access respects the viewer’s donor visibility. Management requires donors_manage/group_all, or donors_view with assignment as this subject’s fundraiser; strategy fields follow the same authority. Requires permission: donors_view; plus one of donors_view, donors_manage.
curl --request PUT \
--url https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"version": 1,
"tier": "A",
"goal_amount": 25,
"target_ask_amount": 25,
"expected_giving_month": 1,
"expected_giving_note": "Expected Giving Note",
"strategy_summary": "Strategy Summary",
"interests_summary": "Interests Summary",
"communication_guidance": "Communication Guidance",
"action_donor_id": 1,
"next_review_at": "2026-10-20T12:00:00Z"
}
'import requests
url = "https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}"
payload = {
"version": 1,
"tier": "A",
"goal_amount": 25,
"target_ask_amount": 25,
"expected_giving_month": 1,
"expected_giving_note": "Expected Giving Note",
"strategy_summary": "Strategy Summary",
"interests_summary": "Interests Summary",
"communication_guidance": "Communication Guidance",
"action_donor_id": 1,
"next_review_at": "2026-10-20T12:00:00Z"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
version: 1,
tier: 'A',
goal_amount: 25,
target_ask_amount: 25,
expected_giving_month: 1,
expected_giving_note: 'Expected Giving Note',
strategy_summary: 'Strategy Summary',
interests_summary: 'Interests Summary',
communication_guidance: 'Communication Guidance',
action_donor_id: 1,
next_review_at: '2026-10-20T12:00:00Z'
})
};
fetch('https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}', 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/fundraising/stewardship-plans/{plan_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'version' => 1,
'tier' => 'A',
'goal_amount' => 25,
'target_ask_amount' => 25,
'expected_giving_month' => 1,
'expected_giving_note' => 'Expected Giving Note',
'strategy_summary' => 'Strategy Summary',
'interests_summary' => 'Interests Summary',
'communication_guidance' => 'Communication Guidance',
'action_donor_id' => 1,
'next_review_at' => '2026-10-20T12:00:00Z'
]),
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/fundraising/stewardship-plans/{plan_id}"
payload := strings.NewReader("{\n \"version\": 1,\n \"tier\": \"A\",\n \"goal_amount\": 25,\n \"target_ask_amount\": 25,\n \"expected_giving_month\": 1,\n \"expected_giving_note\": \"Expected Giving Note\",\n \"strategy_summary\": \"Strategy Summary\",\n \"interests_summary\": \"Interests Summary\",\n \"communication_guidance\": \"Communication Guidance\",\n \"action_donor_id\": 1,\n \"next_review_at\": \"2026-10-20T12:00:00Z\"\n}")
req, _ := http.NewRequest("PUT", 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.put("https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"version\": 1,\n \"tier\": \"A\",\n \"goal_amount\": 25,\n \"target_ask_amount\": 25,\n \"expected_giving_month\": 1,\n \"expected_giving_note\": \"Expected Giving Note\",\n \"strategy_summary\": \"Strategy Summary\",\n \"interests_summary\": \"Interests Summary\",\n \"communication_guidance\": \"Communication Guidance\",\n \"action_donor_id\": 1,\n \"next_review_at\": \"2026-10-20T12:00:00Z\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"version\": 1,\n \"tier\": \"A\",\n \"goal_amount\": 25,\n \"target_ask_amount\": 25,\n \"expected_giving_month\": 1,\n \"expected_giving_note\": \"Expected Giving Note\",\n \"strategy_summary\": \"Strategy Summary\",\n \"interests_summary\": \"Interests Summary\",\n \"communication_guidance\": \"Communication Guidance\",\n \"action_donor_id\": 1,\n \"next_review_at\": \"2026-10-20T12:00:00Z\"\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"id": 42,
"version": 2,
"subject": {
"type": "donor",
"id": 42,
"display_name": "Example",
"kind": "Example",
"assigned_fundraiser_user_id": 42,
"relationship_manager": {
"id": 42,
"display_name": "Example"
},
"action_donors": [
{
"id": 42,
"display_name": "Example",
"email": "Example",
"do_not_email": true,
"status": "Example",
"role": "Example",
"relationship_label": "Example",
"is_primary_contact": true
}
],
"available": true,
"archived": true
},
"stage": "qualifying",
"status": "draft",
"tier": "A",
"stage_entered_at": "2026-10-20T12:00:00Z",
"action_donor_id": 42,
"period": {
"start": "2026-10-20",
"end": "2026-10-20"
},
"currency": "USD",
"goal_amount": 1000,
"attributed_actual": 1000,
"direct_actual": 1000,
"soft_credit_actual": 1000,
"variance_amount": 1000,
"gift_count": 2,
"giving_status": "pending",
"giving_last_successful_at": "2026-10-20T12:00:00Z",
"currency_breakdown": [
{
"currency": "Example",
"attributed_actual": 1000,
"direct_actual": 1000,
"soft_credit_actual": 1000,
"gift_count": 2
}
],
"next_move": {
"id": 42,
"title": "Example",
"planned_at": "2026-10-20T12:00:00Z"
},
"open_move_count": 2,
"overdue_move_count": 2,
"health": {
"status": "Example",
"reasons": [
"overdue_move"
]
},
"metric_basis": "relationship_attribution",
"completion_outcome": "Example",
"qualification_template_version": 2,
"qualification_progress": {
"completed": 2,
"skipped": 2,
"total": 2
},
"permissions": {
"can_manage": true,
"can_view_strategy": true
},
"strategy_reviewed_at": "2026-10-20T12:00:00Z",
"next_review_at": "2026-10-20T12:00:00Z",
"target_ask_amount": 1000,
"expected_giving_month": 10,
"expected_giving_note": "Example",
"closure_reason": "Example",
"strategy_summary": "Example",
"interests_summary": "Example",
"communication_guidance": "Example",
"moves": [
{
"id": 42,
"version": 2,
"kind": "cultivation",
"template_key": "Example",
"sequence": 2,
"title": "Example",
"description": "Example",
"planned_at": "2026-10-20T12:00:00Z",
"status": "planned",
"action_donor_id": 42,
"owner_user_id": 42,
"task_id": 42,
"completion_source": "manual",
"outcome_summary": "Example",
"completed_at": "2026-10-20T12:00:00Z",
"status_reason": "Example",
"evidence": [
{
"id": 42,
"type": "activity",
"record_id": 42,
"role": "completion",
"available": true,
"is_contact": true,
"is_meaningful": true,
"meaningful_basis": "Example",
"exclusion_reason": "Example"
}
]
}
],
"reviews": [
{
"id": 42,
"review_type": "strategy",
"outcome": "Example",
"summary": "Example",
"redacted_at": "2026-10-20T12:00:00Z",
"reviewed_at": "2026-10-20T12:00:00Z",
"reviewed_by_user_id": 42
}
],
"warnings": [
{
"code": "work_projection_delayed",
"message": "Example"
}
]
}
}{
"message": "Unauthorized",
"status": "error"
}{
"message": "Not Found",
"status": "error"
}{
"status": "error",
"code": "PLAN_VERSION_CONFLICT",
"message": "Example",
"data": {
"latest": {
"id": 42,
"version": 2,
"subject": {
"type": "donor",
"id": 42
},
"stage": "qualifying",
"status": "draft",
"tier": "A",
"stage_entered_at": "2026-10-20T12:00:00Z",
"action_donor_id": 42,
"period": {
"start": "2026-10-20",
"end": "2026-10-20"
},
"currency": "USD",
"goal_amount": 1000,
"attributed_actual": 1000,
"direct_actual": 1000,
"soft_credit_actual": 1000,
"variance_amount": 1000,
"gift_count": 2,
"giving_status": "pending",
"giving_last_successful_at": "2026-10-20T12:00:00Z",
"currency_breakdown": [
{
"currency": "Example",
"attributed_actual": 1000,
"direct_actual": 1000,
"soft_credit_actual": 1000,
"gift_count": 2
}
],
"next_move": {
"id": 42,
"title": "Example",
"planned_at": "2026-10-20T12:00:00Z"
},
"open_move_count": 2,
"overdue_move_count": 2,
"health": {
"status": "Example",
"reasons": [
"overdue_move"
]
},
"metric_basis": "relationship_attribution"
},
"conflicting_plan": {}
}
}{
"errors": {},
"status": "error",
"message": "The given data was invalid."
}Authorizations
Passport bearer token authentication for legacy admin sessions.
Path Parameters
Plan id in the current organization.
Body
Validated request fields for this operation.
Current plan version for optimistic concurrency. Stale versions return 409.
x >= 11
Tier. Validation: nullable; allowed values: A, B, C.
A, B, C, null "A"
Goal amount. Validation: nullable, numeric, min:0, max:9999999999999.99.
0 <= x <= 9999999999999.9925
Target ask amount. Validation: nullable, numeric, min:0, max:9999999999999.99.
0 <= x <= 9999999999999.9925
Expected giving month. Validation: nullable, integer, between:1,12.
1 <= x <= 121
Expected giving note. Validation: nullable, string, max:255.
255"Expected Giving Note"
Strategy summary. Validation: nullable, string, max:10000.
10000"Strategy Summary"
Interests summary. Validation: nullable, string, max:10000.
10000"Interests Summary"
Communication guidance. Validation: nullable, string, max:10000.
10000"Communication Guidance"
Action donor id. Validation: nullable, integer, min:1.
x >= 11
Next review at. Validation: nullable, date.
"2026-10-20T12:00:00Z"
curl --request PUT \
--url https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"version": 1,
"tier": "A",
"goal_amount": 25,
"target_ask_amount": 25,
"expected_giving_month": 1,
"expected_giving_note": "Expected Giving Note",
"strategy_summary": "Strategy Summary",
"interests_summary": "Interests Summary",
"communication_guidance": "Communication Guidance",
"action_donor_id": 1,
"next_review_at": "2026-10-20T12:00:00Z"
}
'import requests
url = "https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}"
payload = {
"version": 1,
"tier": "A",
"goal_amount": 25,
"target_ask_amount": 25,
"expected_giving_month": 1,
"expected_giving_note": "Expected Giving Note",
"strategy_summary": "Strategy Summary",
"interests_summary": "Interests Summary",
"communication_guidance": "Communication Guidance",
"action_donor_id": 1,
"next_review_at": "2026-10-20T12:00:00Z"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
version: 1,
tier: 'A',
goal_amount: 25,
target_ask_amount: 25,
expected_giving_month: 1,
expected_giving_note: 'Expected Giving Note',
strategy_summary: 'Strategy Summary',
interests_summary: 'Interests Summary',
communication_guidance: 'Communication Guidance',
action_donor_id: 1,
next_review_at: '2026-10-20T12:00:00Z'
})
};
fetch('https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}', 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/fundraising/stewardship-plans/{plan_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'version' => 1,
'tier' => 'A',
'goal_amount' => 25,
'target_ask_amount' => 25,
'expected_giving_month' => 1,
'expected_giving_note' => 'Expected Giving Note',
'strategy_summary' => 'Strategy Summary',
'interests_summary' => 'Interests Summary',
'communication_guidance' => 'Communication Guidance',
'action_donor_id' => 1,
'next_review_at' => '2026-10-20T12:00:00Z'
]),
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/fundraising/stewardship-plans/{plan_id}"
payload := strings.NewReader("{\n \"version\": 1,\n \"tier\": \"A\",\n \"goal_amount\": 25,\n \"target_ask_amount\": 25,\n \"expected_giving_month\": 1,\n \"expected_giving_note\": \"Expected Giving Note\",\n \"strategy_summary\": \"Strategy Summary\",\n \"interests_summary\": \"Interests Summary\",\n \"communication_guidance\": \"Communication Guidance\",\n \"action_donor_id\": 1,\n \"next_review_at\": \"2026-10-20T12:00:00Z\"\n}")
req, _ := http.NewRequest("PUT", 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.put("https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"version\": 1,\n \"tier\": \"A\",\n \"goal_amount\": 25,\n \"target_ask_amount\": 25,\n \"expected_giving_month\": 1,\n \"expected_giving_note\": \"Expected Giving Note\",\n \"strategy_summary\": \"Strategy Summary\",\n \"interests_summary\": \"Interests Summary\",\n \"communication_guidance\": \"Communication Guidance\",\n \"action_donor_id\": 1,\n \"next_review_at\": \"2026-10-20T12:00:00Z\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"version\": 1,\n \"tier\": \"A\",\n \"goal_amount\": 25,\n \"target_ask_amount\": 25,\n \"expected_giving_month\": 1,\n \"expected_giving_note\": \"Expected Giving Note\",\n \"strategy_summary\": \"Strategy Summary\",\n \"interests_summary\": \"Interests Summary\",\n \"communication_guidance\": \"Communication Guidance\",\n \"action_donor_id\": 1,\n \"next_review_at\": \"2026-10-20T12:00:00Z\"\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"id": 42,
"version": 2,
"subject": {
"type": "donor",
"id": 42,
"display_name": "Example",
"kind": "Example",
"assigned_fundraiser_user_id": 42,
"relationship_manager": {
"id": 42,
"display_name": "Example"
},
"action_donors": [
{
"id": 42,
"display_name": "Example",
"email": "Example",
"do_not_email": true,
"status": "Example",
"role": "Example",
"relationship_label": "Example",
"is_primary_contact": true
}
],
"available": true,
"archived": true
},
"stage": "qualifying",
"status": "draft",
"tier": "A",
"stage_entered_at": "2026-10-20T12:00:00Z",
"action_donor_id": 42,
"period": {
"start": "2026-10-20",
"end": "2026-10-20"
},
"currency": "USD",
"goal_amount": 1000,
"attributed_actual": 1000,
"direct_actual": 1000,
"soft_credit_actual": 1000,
"variance_amount": 1000,
"gift_count": 2,
"giving_status": "pending",
"giving_last_successful_at": "2026-10-20T12:00:00Z",
"currency_breakdown": [
{
"currency": "Example",
"attributed_actual": 1000,
"direct_actual": 1000,
"soft_credit_actual": 1000,
"gift_count": 2
}
],
"next_move": {
"id": 42,
"title": "Example",
"planned_at": "2026-10-20T12:00:00Z"
},
"open_move_count": 2,
"overdue_move_count": 2,
"health": {
"status": "Example",
"reasons": [
"overdue_move"
]
},
"metric_basis": "relationship_attribution",
"completion_outcome": "Example",
"qualification_template_version": 2,
"qualification_progress": {
"completed": 2,
"skipped": 2,
"total": 2
},
"permissions": {
"can_manage": true,
"can_view_strategy": true
},
"strategy_reviewed_at": "2026-10-20T12:00:00Z",
"next_review_at": "2026-10-20T12:00:00Z",
"target_ask_amount": 1000,
"expected_giving_month": 10,
"expected_giving_note": "Example",
"closure_reason": "Example",
"strategy_summary": "Example",
"interests_summary": "Example",
"communication_guidance": "Example",
"moves": [
{
"id": 42,
"version": 2,
"kind": "cultivation",
"template_key": "Example",
"sequence": 2,
"title": "Example",
"description": "Example",
"planned_at": "2026-10-20T12:00:00Z",
"status": "planned",
"action_donor_id": 42,
"owner_user_id": 42,
"task_id": 42,
"completion_source": "manual",
"outcome_summary": "Example",
"completed_at": "2026-10-20T12:00:00Z",
"status_reason": "Example",
"evidence": [
{
"id": 42,
"type": "activity",
"record_id": 42,
"role": "completion",
"available": true,
"is_contact": true,
"is_meaningful": true,
"meaningful_basis": "Example",
"exclusion_reason": "Example"
}
]
}
],
"reviews": [
{
"id": 42,
"review_type": "strategy",
"outcome": "Example",
"summary": "Example",
"redacted_at": "2026-10-20T12:00:00Z",
"reviewed_at": "2026-10-20T12:00:00Z",
"reviewed_by_user_id": 42
}
],
"warnings": [
{
"code": "work_projection_delayed",
"message": "Example"
}
]
}
}{
"message": "Unauthorized",
"status": "error"
}{
"message": "Not Found",
"status": "error"
}{
"status": "error",
"code": "PLAN_VERSION_CONFLICT",
"message": "Example",
"data": {
"latest": {
"id": 42,
"version": 2,
"subject": {
"type": "donor",
"id": 42
},
"stage": "qualifying",
"status": "draft",
"tier": "A",
"stage_entered_at": "2026-10-20T12:00:00Z",
"action_donor_id": 42,
"period": {
"start": "2026-10-20",
"end": "2026-10-20"
},
"currency": "USD",
"goal_amount": 1000,
"attributed_actual": 1000,
"direct_actual": 1000,
"soft_credit_actual": 1000,
"variance_amount": 1000,
"gift_count": 2,
"giving_status": "pending",
"giving_last_successful_at": "2026-10-20T12:00:00Z",
"currency_breakdown": [
{
"currency": "Example",
"attributed_actual": 1000,
"direct_actual": 1000,
"soft_credit_actual": 1000,
"gift_count": 2
}
],
"next_move": {
"id": 42,
"title": "Example",
"planned_at": "2026-10-20T12:00:00Z"
},
"open_move_count": 2,
"overdue_move_count": 2,
"health": {
"status": "Example",
"reasons": [
"overdue_move"
]
},
"metric_basis": "relationship_attribution"
},
"conflicting_plan": {}
}
}{
"errors": {},
"status": "error",
"message": "The given data was invalid."
}