Log a donor mailing manually
Records a manual mailing activity for this organization active donor, with an optional historical logged_at (defaults now), notes and label layout. Creates an activity record without generating or sending artifacts. Requires permission: one of mailings_generate, mailings_manage, manage_email.
curl --request POST \
--url https://api.gratona.com/v1/admin/donors/{donor_id}/mailings \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"mailing_name": "Mailing Name",
"preset_key": "Preset Key",
"label_layout_preset": "single_label",
"logged_at": "2026-10-20T12:00:00Z",
"notes": "Notes"
}
'import requests
url = "https://api.gratona.com/v1/admin/donors/{donor_id}/mailings"
payload = {
"mailing_name": "Mailing Name",
"preset_key": "Preset Key",
"label_layout_preset": "single_label",
"logged_at": "2026-10-20T12:00:00Z",
"notes": "Notes"
}
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({
mailing_name: 'Mailing Name',
preset_key: 'Preset Key',
label_layout_preset: 'single_label',
logged_at: '2026-10-20T12:00:00Z',
notes: 'Notes'
})
};
fetch('https://api.gratona.com/v1/admin/donors/{donor_id}/mailings', 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/donors/{donor_id}/mailings",
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([
'mailing_name' => 'Mailing Name',
'preset_key' => 'Preset Key',
'label_layout_preset' => 'single_label',
'logged_at' => '2026-10-20T12:00:00Z',
'notes' => 'Notes'
]),
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/donors/{donor_id}/mailings"
payload := strings.NewReader("{\n \"mailing_name\": \"Mailing Name\",\n \"preset_key\": \"Preset Key\",\n \"label_layout_preset\": \"single_label\",\n \"logged_at\": \"2026-10-20T12:00:00Z\",\n \"notes\": \"Notes\"\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/donors/{donor_id}/mailings")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"mailing_name\": \"Mailing Name\",\n \"preset_key\": \"Preset Key\",\n \"label_layout_preset\": \"single_label\",\n \"logged_at\": \"2026-10-20T12:00:00Z\",\n \"notes\": \"Notes\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gratona.com/v1/admin/donors/{donor_id}/mailings")
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 \"mailing_name\": \"Mailing Name\",\n \"preset_key\": \"Preset Key\",\n \"label_layout_preset\": \"single_label\",\n \"logged_at\": \"2026-10-20T12:00:00Z\",\n \"notes\": \"Notes\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": 42,
"donor_id": 42,
"action": "donor_mailing_logged",
"source": "manual",
"mailing_name": "October impact letter",
"preset_key": "Example",
"label_layout_preset": "single_label",
"notes": "Example",
"logged_at": "2026-10-20T12:00:00Z",
"description": "Example",
"created_at": "2026-10-20T12:00:00Z",
"generated_mailing_id": 42,
"mailing_run_id": 42,
"mailing_template_id": 42,
"recipient_name": "Example",
"has_letter_artifact": true,
"has_label_artifact": true,
"has_label_sheet_artifact": true,
"has_run_zip_artifact": true,
"letter_url": "/admin/mailings/42/artifact/letter",
"label_url": "/admin/mailings/42/artifact/letter",
"label_sheet_url": "/admin/mailings/42/artifact/letter",
"run_zip_url": "/admin/mailings/42/artifact/letter",
"open_url": "/admin/mailings/42/artifact/letter"
}
}{
"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
Donor id in the current organization.
Body
Validated request fields for this operation.
Mailing name. Validation: required, string, max:191.
191"Mailing Name"
Preset key. Validation: nullable, string, max:64.
64"Preset Key"
Label layout preset. Validation: nullable; allowed values: single_label, a4_sheet, a3_sheet, letter_85x11_1_up, letter_4x2_10_up, letter_2625x1_30_up, letter_3x2_10_up.
single_label, a4_sheet, a3_sheet, letter_85x11_1_up, letter_4x2_10_up, letter_2625x1_30_up, letter_3x2_10_up, null "single_label"
Logged at. Validation: nullable, date.
"2026-10-20T12:00:00Z"
Notes. Validation: nullable, string.
"Notes"
Response
Operation result.
Manual log or generated mailing activity. Manual logs normally have no generated artifact identifiers/URLs.
Show child attributes
Show child attributes
curl --request POST \
--url https://api.gratona.com/v1/admin/donors/{donor_id}/mailings \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"mailing_name": "Mailing Name",
"preset_key": "Preset Key",
"label_layout_preset": "single_label",
"logged_at": "2026-10-20T12:00:00Z",
"notes": "Notes"
}
'import requests
url = "https://api.gratona.com/v1/admin/donors/{donor_id}/mailings"
payload = {
"mailing_name": "Mailing Name",
"preset_key": "Preset Key",
"label_layout_preset": "single_label",
"logged_at": "2026-10-20T12:00:00Z",
"notes": "Notes"
}
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({
mailing_name: 'Mailing Name',
preset_key: 'Preset Key',
label_layout_preset: 'single_label',
logged_at: '2026-10-20T12:00:00Z',
notes: 'Notes'
})
};
fetch('https://api.gratona.com/v1/admin/donors/{donor_id}/mailings', 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/donors/{donor_id}/mailings",
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([
'mailing_name' => 'Mailing Name',
'preset_key' => 'Preset Key',
'label_layout_preset' => 'single_label',
'logged_at' => '2026-10-20T12:00:00Z',
'notes' => 'Notes'
]),
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/donors/{donor_id}/mailings"
payload := strings.NewReader("{\n \"mailing_name\": \"Mailing Name\",\n \"preset_key\": \"Preset Key\",\n \"label_layout_preset\": \"single_label\",\n \"logged_at\": \"2026-10-20T12:00:00Z\",\n \"notes\": \"Notes\"\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/donors/{donor_id}/mailings")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"mailing_name\": \"Mailing Name\",\n \"preset_key\": \"Preset Key\",\n \"label_layout_preset\": \"single_label\",\n \"logged_at\": \"2026-10-20T12:00:00Z\",\n \"notes\": \"Notes\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gratona.com/v1/admin/donors/{donor_id}/mailings")
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 \"mailing_name\": \"Mailing Name\",\n \"preset_key\": \"Preset Key\",\n \"label_layout_preset\": \"single_label\",\n \"logged_at\": \"2026-10-20T12:00:00Z\",\n \"notes\": \"Notes\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": 42,
"donor_id": 42,
"action": "donor_mailing_logged",
"source": "manual",
"mailing_name": "October impact letter",
"preset_key": "Example",
"label_layout_preset": "single_label",
"notes": "Example",
"logged_at": "2026-10-20T12:00:00Z",
"description": "Example",
"created_at": "2026-10-20T12:00:00Z",
"generated_mailing_id": 42,
"mailing_run_id": 42,
"mailing_template_id": 42,
"recipient_name": "Example",
"has_letter_artifact": true,
"has_label_artifact": true,
"has_label_sheet_artifact": true,
"has_run_zip_artifact": true,
"letter_url": "/admin/mailings/42/artifact/letter",
"label_url": "/admin/mailings/42/artifact/letter",
"label_sheet_url": "/admin/mailings/42/artifact/letter",
"run_zip_url": "/admin/mailings/42/artifact/letter",
"open_url": "/admin/mailings/42/artifact/letter"
}
}{
"message": "Unauthorized",
"status": "error"
}{
"message": "Not Found",
"status": "error"
}{
"errors": {},
"status": "error",
"message": "The given data was invalid."
}