Restore stewardship-move Work
Uses the move version. Clears the Work snooze and refreshes its projection without reopening the underlying move status. 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 POST \
--url https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}/moves/{move_id}/work/reopen \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"version": 1
}'import requests
url = "https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}/moves/{move_id}/work/reopen"
payload = { "version": 1 }
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({version: 1})
};
fetch('https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}/moves/{move_id}/work/reopen', 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}/moves/{move_id}/work/reopen",
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([
'version' => 1
]),
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}/moves/{move_id}/work/reopen"
payload := strings.NewReader("{\n \"version\": 1\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/fundraising/stewardship-plans/{plan_id}/moves/{move_id}/work/reopen")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"version\": 1\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}/moves/{move_id}/work/reopen")
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 \"version\": 1\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"status": "reopened",
"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.
Move id in the current organization.
Body
Validated request fields for this operation.
Current move version for optimistic concurrency. Stale versions return 409.
x >= 11
curl --request POST \
--url https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}/moves/{move_id}/work/reopen \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"version": 1
}'import requests
url = "https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}/moves/{move_id}/work/reopen"
payload = { "version": 1 }
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({version: 1})
};
fetch('https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}/moves/{move_id}/work/reopen', 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}/moves/{move_id}/work/reopen",
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([
'version' => 1
]),
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}/moves/{move_id}/work/reopen"
payload := strings.NewReader("{\n \"version\": 1\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/fundraising/stewardship-plans/{plan_id}/moves/{move_id}/work/reopen")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"version\": 1\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gratona.com/v1/admin/fundraising/stewardship-plans/{plan_id}/moves/{move_id}/work/reopen")
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 \"version\": 1\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"status": "reopened",
"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."
}