Undo a local gift-owner or keep-local decision
Requires a completed matching local reconcile run that has not already been undone. Rechecks gifts, source mappings, controls and related work under lock. Owner undo restores original donors only while records are still waiting and undispatched. Keep-local undo revokes controls/restores safe local work; recorded deferred refunds keep affected gifts waiting for refund reconciliation and disable their retries. No provider requests are sent; changed or processing records reject the whole batch. Requires the integration platform global/configured client feature gate (normally integration_platform_foundation). Requires permission: one of access_account_settings.
curl --request POST \
--url https://api.gratona.com/v1/integrations/hub/connections/{connection_id}/outbound-donation-owner-resolutions/{run_id}/undo \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.gratona.com/v1/integrations/hub/connections/{connection_id}/outbound-donation-owner-resolutions/{run_id}/undo"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.gratona.com/v1/integrations/hub/connections/{connection_id}/outbound-donation-owner-resolutions/{run_id}/undo', 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/integrations/hub/connections/{connection_id}/outbound-donation-owner-resolutions/{run_id}/undo",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.gratona.com/v1/integrations/hub/connections/{connection_id}/outbound-donation-owner-resolutions/{run_id}/undo"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
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/integrations/hub/connections/{connection_id}/outbound-donation-owner-resolutions/{run_id}/undo")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gratona.com/v1/integrations/hub/connections/{connection_id}/outbound-donation-owner-resolutions/{run_id}/undo")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"run_id": 42,
"undone_run_id": 42,
"workflow": "undo_outbound_donation_owner_resolution",
"record_count": 2,
"source_donor_ids": [
42
],
"target_donor_ids": [
42
],
"side_effects": {
"gifts_changed": 2,
"donors_created": 0,
"donations_created": 0,
"payments_changed": 0,
"messages_sent": 0,
"provider_requests_sent": 0,
"sync_cursors_changed": 0,
"refunds_held_for_review": 2
},
"preview": {
"connection_id": 42,
"decision_count": 2,
"safe_decision_count": 2,
"record_count": 2,
"total_amount": "125.00",
"currency": "Example",
"decisions": [
{
"current_owner": {
"id": 42,
"name": "Example",
"email": "Example",
"username": "Example",
"archived_at": "2023-11-07T05:31:56Z",
"profile": {
"display_name": "Example",
"email": "Example",
"username": "Example",
"organization_name": "Example"
}
},
"record_count": 2,
"total_amount": "125.00",
"currency": "Example",
"currencies": [
"<string>"
],
"date_from": "2023-11-07T05:31:56Z",
"date_to": "2023-11-07T05:31:56Z",
"donation_ids": [
42
],
"outbox_event_ids": [
42
],
"candidates": [
{
"id": 42,
"name": "Example",
"email": "Example",
"username": "Example",
"provider_object_id": "Example",
"mapping_id": 42,
"mapping_updated_at": "2023-11-07T05:31:56Z",
"profile": {
"display_name": "Example",
"email": "Example",
"username": "Example",
"organization_name": "Example"
},
"match_signals": [
"display_name"
],
"has_open_identity_conflict": true,
"safe_to_select": true
}
],
"safe_to_select": true,
"safe_to_keep_local": true,
"blockers": [
"<string>"
],
"keep_local_blockers": [
"<string>"
],
"review_token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
],
"side_effects": {
"gifts_changed": 2,
"donors_created": 0,
"donations_created": 0,
"payments_changed": 0,
"messages_sent": 0,
"provider_requests_sent": 0,
"sync_cursors_changed": 0,
"refunds_held_for_review": 2
},
"recent_resolution": {
"run_id": 42,
"decision": "keep_local",
"record_count": 2,
"total_amount": "125.00",
"currency": "Example",
"created_at": "2023-11-07T05:31:56Z"
}
}
},
"message": "Example"
}{
"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
Connection id in the current organization.
Run id in the current organization.
curl --request POST \
--url https://api.gratona.com/v1/integrations/hub/connections/{connection_id}/outbound-donation-owner-resolutions/{run_id}/undo \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.gratona.com/v1/integrations/hub/connections/{connection_id}/outbound-donation-owner-resolutions/{run_id}/undo"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.gratona.com/v1/integrations/hub/connections/{connection_id}/outbound-donation-owner-resolutions/{run_id}/undo', 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/integrations/hub/connections/{connection_id}/outbound-donation-owner-resolutions/{run_id}/undo",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.gratona.com/v1/integrations/hub/connections/{connection_id}/outbound-donation-owner-resolutions/{run_id}/undo"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
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/integrations/hub/connections/{connection_id}/outbound-donation-owner-resolutions/{run_id}/undo")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gratona.com/v1/integrations/hub/connections/{connection_id}/outbound-donation-owner-resolutions/{run_id}/undo")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"run_id": 42,
"undone_run_id": 42,
"workflow": "undo_outbound_donation_owner_resolution",
"record_count": 2,
"source_donor_ids": [
42
],
"target_donor_ids": [
42
],
"side_effects": {
"gifts_changed": 2,
"donors_created": 0,
"donations_created": 0,
"payments_changed": 0,
"messages_sent": 0,
"provider_requests_sent": 0,
"sync_cursors_changed": 0,
"refunds_held_for_review": 2
},
"preview": {
"connection_id": 42,
"decision_count": 2,
"safe_decision_count": 2,
"record_count": 2,
"total_amount": "125.00",
"currency": "Example",
"decisions": [
{
"current_owner": {
"id": 42,
"name": "Example",
"email": "Example",
"username": "Example",
"archived_at": "2023-11-07T05:31:56Z",
"profile": {
"display_name": "Example",
"email": "Example",
"username": "Example",
"organization_name": "Example"
}
},
"record_count": 2,
"total_amount": "125.00",
"currency": "Example",
"currencies": [
"<string>"
],
"date_from": "2023-11-07T05:31:56Z",
"date_to": "2023-11-07T05:31:56Z",
"donation_ids": [
42
],
"outbox_event_ids": [
42
],
"candidates": [
{
"id": 42,
"name": "Example",
"email": "Example",
"username": "Example",
"provider_object_id": "Example",
"mapping_id": 42,
"mapping_updated_at": "2023-11-07T05:31:56Z",
"profile": {
"display_name": "Example",
"email": "Example",
"username": "Example",
"organization_name": "Example"
},
"match_signals": [
"display_name"
],
"has_open_identity_conflict": true,
"safe_to_select": true
}
],
"safe_to_select": true,
"safe_to_keep_local": true,
"blockers": [
"<string>"
],
"keep_local_blockers": [
"<string>"
],
"review_token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
],
"side_effects": {
"gifts_changed": 2,
"donors_created": 0,
"donations_created": 0,
"payments_changed": 0,
"messages_sent": 0,
"provider_requests_sent": 0,
"sync_cursors_changed": 0,
"refunds_held_for_review": 2
},
"recent_resolution": {
"run_id": 42,
"decision": "keep_local",
"record_count": 2,
"total_amount": "125.00",
"currency": "Example",
"created_at": "2023-11-07T05:31:56Z"
}
}
},
"message": "Example"
}{
"message": "Unauthorized",
"status": "error"
}{
"message": "Not Found",
"status": "error"
}{
"errors": {},
"status": "error",
"message": "The given data was invalid."
}