Webhooks
Webhooks tell your system when something changes in Operation Charity: a new contact, a gift, a receipt. We send an HTTPS POST with a signed JSON body to each endpoint that asked for that event.
Set up an endpoint
- An admin opens Settings → Webhooks and adds your HTTPS URL with the events you want (or calls
POST /v1/webhook-subscriptions). - Copy the signing secret. It is shown once; rotating it shows a new one.
- Send a test delivery and check it arrives. Each delivery, with its attempts and the last response, is listed under the endpoint.
Answer with any 2xx status within 10 seconds. Do slow work after you answer. Redirects are not followed.
What we send
Signed delivery envelope. Deliveries are at-least-once; consumers dedupe on id. Failed deliveries (non-2xx or timeout after 10 s) are retried with exponential backoff (1 min, 5 min, 30 min, 2 h, 6 h, 12 h, 24 h) and then dropped; a subscription that fails every delivery for 3 days is set inactive. Verify X-OC-Signature before trusting the body.
| Field | Type | Description |
|---|---|---|
id | string (uuid) | Event id, stable across retries. |
type | one of: organization.updated, user.created, user.updated, contact.created, contact.updated, contact.deleted, tag.created, tag.updated, tag.deleted, custom_field_definition.created, custom_field_definition.updated, custom_field_definition.deleted, saved_list.created, saved_list.updated, saved_list.deleted, saved_list_snapshot.created, household.created, household.updated, household.deleted, contact_relationship.created, contact_relationship.updated, contact_relationship.deleted, fund.created, fund.updated, fund.deleted, campaign.created, campaign.updated, campaign.deleted, appeal.created, appeal.updated, appeal.deleted, soft_credit.created, soft_credit.updated, soft_credit.deleted, grant.created, grant.updated, grant.deleted, communication.created, communication.updated, communication.deleted, task.created, task.updated, task.deleted, gift.created, gift.updated, gift_adjustment.created, gift_batch.created, gift_batch.updated, gift_batch.deleted, gift_batch.closed, gift_batch.reopened, receipt.issued, receipt.sent, import_job.committed, import_job.rolled_back, import_job.discarded, donation_form.created, donation_form.updated, donation_form.deleted, recurring_donation.created, recurring_donation.updated, welcome_enrollment.created, welcome_enrollment.updated, email_template.created, email_template.updated, email_template.deleted, email_send.created, email_send.updated, email_send.deleted, webhook.test | Every event the platform emits. See the `webhooks` section for payloads. |
created_at | string (date-time) | |
organization_id | string (uuid) | |
data | object | The resource after the change (for deleted, its last state). |
Headers
X-OC-SignatureWebhook signature:
t=<unix seconds>,v1=<hex HMAC-SHA256>. The HMAC key is the subscription secret and the signed string is${t}.${raw body}. Reject deliveries whosetis more than 5 minutes old. For 24 hours after a secret rotation the header carries twov1=entries (new secret first, then the previous one); accept the delivery when any entry matches.Example:
t=1759674600,v1=5f1c7e0f6a1d0f3c7e2a9b4d8c6e1f0a2b3c4d5e6f708192a3b4c5d6e7f80912X-OC-Event-IdThe event id, the same as the body's
idand stable across retries. Dedupe on it (C4, ADR-011).Example:
3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e39X-OC-Event-Type,X-OC-Delivery-AttemptThe event type, and which attempt this is (1 for the first). Handy for logs; the signature does not cover headers.
Check the signature
Every delivery is signed with your endpoint's secret: HMAC-SHA256 over ${t}.${raw body}, where t is the Unix time in the header. Check it on the raw body before you parse it, and reject a t more than 5 minutes away from your clock. For 24 hours after you rotate the secret, the header carries two v1= entries (new secret first, then the old one), so either secret works while you switch.
import crypto from "node:crypto";
// rawBody: the request body exactly as received (a string), before any JSON parsing.
export function verify(header, secret, rawBody) {
const parts = header.split(",").map((p) => p.trim());
const t = Number(parts.find((p) => p.startsWith("t="))?.slice(2));
// Reject old or future timestamps (5 minutes) so a captured request cannot be replayed later.
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = Buffer.from(crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex"));
// During a secret rotation there are two v1= entries; any match is enough.
return parts
.filter((p) => p.startsWith("v1="))
.some((p) => {
const got = Buffer.from(p.slice(3));
return got.length === expected.length && crypto.timingSafeEqual(got, expected);
});
}
// Express example: keep the raw body for verification.
app.post("/operation-charity", express.text({ type: "application/json" }), (req, res) => {
if (!verify(req.get("X-OC-Signature") ?? "", process.env.OC_WEBHOOK_SECRET, req.body)) return res.sendStatus(400);
const event = JSON.parse(req.body);
// Deliveries can repeat: skip event ids you have already handled.
res.sendStatus(200);
});import hashlib
import hmac
import time
def verify(header: str, secret: str, raw_body: bytes) -> bool:
parts = [p.strip() for p in header.split(",")]
try:
t = int(next(p[2:] for p in parts if p.startswith("t=")))
except (StopIteration, ValueError):
return False
# Reject old or future timestamps (5 minutes) so a captured request cannot be replayed later.
if abs(time.time() - t) > 300:
return False
signed = str(t).encode() + b"." + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
# During a secret rotation there are two v1= entries; any match is enough.
return any(hmac.compare_digest(p[3:], expected) for p in parts if p.startswith("v1="))
# Flask example: request.get_data() is the raw body.
import os
from flask import Flask, request
app = Flask(__name__)
: request.get_data() is the raw body.
@app.post("/operation-charity")
def webhook():
if not verify(request.headers.get("X-OC-Signature", ""), os.environ["OC_WEBHOOK_SECRET"], request.get_data()):
return "", 400
event = request.get_json()
# Deliveries can repeat: skip event ids you have already handled.
return "", 200Retries and duplicates
If your endpoint does not answer with a 2xx, we try again after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours, 24 hours. After the eighth attempt the delivery is marked failed; an admin can retry it from the deliveries list.
An endpoint whose deliveries have all failed for 3 days is paused. Turn it back on in Settings → Webhooks once it is fixed.
A delivery can arrive more than once. Use the event id (also in X-OC-Event-Id) to skip events you have already handled. Events can arrive out of order; compare created_at or fetch the record when order matters.
We only deliver to public HTTPS addresses. URLs that point to private, local or reserved network addresses are refused.
Events
70 event types. Changes to API keys and webhook endpoints are recorded in the audit log but send no events. See the API reference for each record's fields.
organization
organization.updated
data is the full organization as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e01",
"type": "organization.updated",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"name": "Maple Street Food Bank",
"ein": "84-1234567",
"timezone": "America/Denver",
"fiscal_year_start_month": 7,
"mailing_address": "1200 Maple Street\nDenver, CO 80202",
"receipt_signature_name": "Rosa Diaz",
"receipt_signature_title": "Executive director",
"receipt_footer": "Questions about your gift? Call 303-555-0100.",
"intangible_religious_benefits": false,
"receipt_logo_url": null,
"require_two_step": false,
"created_at": "2026-01-15T16:00:00Z",
"updated_at": "2026-10-05T14:30:00Z"
}
}user
user.created
data is the full user as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e02",
"type": "user.created",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"email": "luis@maplestreetfoodbank.org",
"full_name": "Luis Ortega",
"role": "staff",
"created_at": "2026-02-02T18:12:00Z",
"updated_at": "2026-02-02T18:12:00Z",
"deactivated_at": null,
"deactivated_by": null,
"invitation": null
}
}user.updated
data is the full user as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e03",
"type": "user.updated",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"email": "luis@maplestreetfoodbank.org",
"full_name": "Luis Ortega",
"role": "staff",
"created_at": "2026-02-02T18:12:00Z",
"updated_at": "2026-02-02T18:12:00Z",
"deactivated_at": null,
"deactivated_by": null,
"invitation": null
}
}contact
contact.created
data is the full contact as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e04",
"type": "contact.created",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"kind": "person",
"first_name": "Aiko",
"last_name": "Nakamura",
"org_name": null,
"display_name": "Aiko Nakamura",
"email": "aiko.nakamura@example.com",
"phone": "+1 303 555 0142",
"household_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d04",
"address_line1": "1420 Spruce St",
"address_line2": null,
"city": "Denver",
"region": "CO",
"postal_code": "80205",
"country": "US",
"employer": "Front Range Dental",
"do_not_contact": false,
"email_opt_in": true,
"sms_opt_in": false,
"source": "form",
"source_ref": "donation-form",
"consented": true,
"custom_fields": {
"volunteer": true,
"t_shirt_size": "M"
},
"tags": [
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7a01",
"name": "Volunteer",
"color": "green"
}
],
"created_at": "2026-03-01T15:00:00Z",
"updated_at": "2026-10-05T14:30:00Z",
"deleted_at": null,
"deleted_by": null
}
}contact.updated
data is the full contact as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e05",
"type": "contact.updated",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"kind": "person",
"first_name": "Aiko",
"last_name": "Nakamura",
"org_name": null,
"display_name": "Aiko Nakamura",
"email": "aiko.nakamura@example.com",
"phone": "+1 303 555 0142",
"household_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d04",
"address_line1": "1420 Spruce St",
"address_line2": null,
"city": "Denver",
"region": "CO",
"postal_code": "80205",
"country": "US",
"employer": "Front Range Dental",
"do_not_contact": false,
"email_opt_in": true,
"sms_opt_in": false,
"source": "form",
"source_ref": "donation-form",
"consented": true,
"custom_fields": {
"volunteer": true,
"t_shirt_size": "M"
},
"tags": [
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7a01",
"name": "Volunteer",
"color": "green"
}
],
"created_at": "2026-03-01T15:00:00Z",
"updated_at": "2026-10-05T14:30:00Z",
"deleted_at": null,
"deleted_by": null
}
}contact.deleted
data is the row with deleted_at set (soft delete, ADR-005). A restore emits contact.updated with deleted_at null.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e06",
"type": "contact.deleted",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"kind": "person",
"first_name": "Aiko",
"last_name": "Nakamura",
"org_name": null,
"display_name": "Aiko Nakamura",
"email": "aiko.nakamura@example.com",
"phone": "+1 303 555 0142",
"household_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d04",
"address_line1": "1420 Spruce St",
"address_line2": null,
"city": "Denver",
"region": "CO",
"postal_code": "80205",
"country": "US",
"employer": "Front Range Dental",
"do_not_contact": false,
"email_opt_in": true,
"sms_opt_in": false,
"source": "form",
"source_ref": "donation-form",
"consented": true,
"custom_fields": {
"volunteer": true,
"t_shirt_size": "M"
},
"tags": [
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7a01",
"name": "Volunteer",
"color": "green"
}
],
"created_at": "2026-03-01T15:00:00Z",
"updated_at": "2026-10-05T16:00:00Z",
"deleted_at": "2026-10-05T16:00:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02"
}
}tag
tag.created
data is the full tag as returned by GET (ADR-018).
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e49",
"type": "tag.created",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7a01",
"name": "Volunteer",
"color": "green",
"contact_count": 23,
"created_at": "2026-09-01T16:00:00Z",
"updated_at": "2026-09-01T16:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}tag.updated
data is the full tag as returned by GET, after a rename or a new color. Every contact with the tag now shows it so; no contact.updated is sent for them.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e50",
"type": "tag.updated",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7a02",
"name": "Board",
"color": "blue",
"contact_count": 7,
"created_at": "2026-09-01T16:00:00Z",
"updated_at": "2026-10-06T09:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}tag.deleted
data is the row with deleted_at set (soft delete, ADR-005). The tag leaves every contact's tags until it is restored; no contact.updated is sent for them. A restore emits tag.updated with deleted_at null, and the tag is back on every contact that had it.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e51",
"type": "tag.deleted",
"created_at": "2026-10-05T16:00:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7a02",
"name": "Board",
"color": "blue",
"contact_count": 7,
"created_at": "2026-09-01T16:00:00Z",
"updated_at": "2026-10-05T16:00:00Z",
"deleted_at": "2026-10-05T16:00:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02"
}
}custom field definition
custom_field_definition.created
data is the full custom field as returned by GET (ADR-018).
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e53",
"type": "custom_field_definition.created",
"created_at": "2026-09-01T16:00:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7b01",
"key": "t_shirt_size",
"label": "T-shirt size",
"type": "choice",
"choices": [
"S",
"M",
"L",
"XL"
],
"position": 0,
"value_count": 41,
"mismatch_count": 2,
"archived_at": null,
"archived_by": null,
"created_at": "2026-09-01T16:00:00Z",
"updated_at": "2026-09-01T16:00:00Z"
}
}custom_field_definition.updated
data is the full custom field as returned by GET, after a new label, type, choices or position (a field moved by another's move included), or an unarchive (archived_at null).
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e54",
"type": "custom_field_definition.updated",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7b02",
"key": "volunteer",
"label": "Volunteers with us",
"type": "yes_no",
"choices": null,
"position": 1,
"value_count": 23,
"mismatch_count": 0,
"archived_at": null,
"archived_by": null,
"created_at": "2026-09-01T16:05:00Z",
"updated_at": "2026-09-01T16:05:00Z"
}
}custom_field_definition.deleted
The field was archived, this resource's delete: data is the field with archived_at set. Every saved value stays on its contacts. Unarchiving emits custom_field_definition.updated with archived_at null, as a restore does (ADR-005).
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e55",
"type": "custom_field_definition.deleted",
"created_at": "2026-10-05T16:00:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7b03",
"key": "newsletter_format",
"label": "Newsletter format",
"type": "text",
"choices": null,
"position": 2,
"value_count": 12,
"mismatch_count": 0,
"archived_at": "2026-10-05T16:00:00Z",
"archived_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"created_at": "2026-09-01T16:00:00Z",
"updated_at": "2026-10-05T16:00:00Z"
}
}saved list
saved_list.created
data is the saved list as GET returns it, without member_count (ADR-020). Membership changes emit nothing.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e70",
"type": "saved_list.created",
"created_at": "2026-10-01T16:00:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7c01",
"name": "Lapsed shelter donors",
"description": "Gave to the shelter last fiscal year, not yet this year, and can be mailed.",
"filter": {
"version": 1,
"match": "all",
"conditions": [
{
"field": "gave",
"period": {
"basis": "fiscal",
"from": {
"type": "year_start",
"years_ago": 1
},
"to": {
"type": "year_end",
"years_ago": 1
}
},
"fund_ids": [
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09"
]
},
{
"field": "gave",
"period": {
"basis": "fiscal",
"from": {
"type": "year_start",
"years_ago": 0
},
"to": null
},
"not": true
},
{
"field": "has_mailing_address"
},
{
"field": "do_not_contact",
"not": true
},
{
"field": "tags",
"match": "none",
"tag_ids": [
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7a02"
]
}
]
},
"version": 3,
"problems": [],
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"created_at": "2026-10-01T16:00:00Z",
"updated_at": "2026-10-05T14:30:00Z",
"deleted_at": null,
"deleted_by": null
}
}saved_list.updated
data is the saved list as GET returns it, after a rename, a new description or a new filter (with its new version), or a restore (deleted_at null).
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e71",
"type": "saved_list.updated",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7c01",
"name": "Lapsed shelter donors",
"description": "Gave to the shelter last fiscal year, not yet this year, and can be mailed.",
"filter": {
"version": 1,
"match": "all",
"conditions": [
{
"field": "gave",
"period": {
"basis": "fiscal",
"from": {
"type": "year_start",
"years_ago": 1
},
"to": {
"type": "year_end",
"years_ago": 1
}
},
"fund_ids": [
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09"
]
},
{
"field": "gave",
"period": {
"basis": "fiscal",
"from": {
"type": "year_start",
"years_ago": 0
},
"to": null
},
"not": true
},
{
"field": "has_mailing_address"
},
{
"field": "do_not_contact",
"not": true
},
{
"field": "tags",
"match": "none",
"tag_ids": [
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7a02"
]
}
]
},
"version": 3,
"problems": [],
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"created_at": "2026-10-01T16:00:00Z",
"updated_at": "2026-10-05T14:30:00Z",
"deleted_at": null,
"deleted_by": null
}
}saved_list.deleted
data is the row with deleted_at set (soft delete, ADR-005). A restore emits saved_list.updated with deleted_at null.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e72",
"type": "saved_list.deleted",
"created_at": "2026-10-05T16:00:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7c02",
"name": "Major donors in Denver",
"description": null,
"filter": {
"version": 1,
"match": "all",
"conditions": [
{
"field": "lifetime_giving",
"min_cents": 100000
},
{
"field": "city",
"values": [
"Denver"
]
},
{
"field": "tags",
"match": "none",
"tag_ids": [
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7a02"
]
}
]
},
"version": 1,
"problems": [],
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"created_at": "2026-10-02T16:00:00Z",
"updated_at": "2026-10-05T16:00:00Z",
"deleted_at": "2026-10-05T16:00:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02"
}
}saved list snapshot
saved_list_snapshot.created
data is the snapshot as GET returns it, without its members (ADR-020); GET /v1/contacts?snapshot_id= lists them.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e73",
"type": "saved_list_snapshot.created",
"created_at": "2026-10-13T15:00:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7d01",
"saved_list_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7c01",
"list_version": 3,
"name": "November appeal",
"filter": {
"version": 1,
"match": "all",
"conditions": [
{
"field": "gave",
"period": {
"basis": "fiscal",
"from": {
"type": "date",
"date": "2025-07-01"
},
"to": {
"type": "date",
"date": "2026-06-30"
}
},
"fund_ids": [
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09"
]
},
{
"field": "gave",
"period": {
"basis": "fiscal",
"from": {
"type": "date",
"date": "2026-07-01"
},
"to": null
},
"not": true
},
{
"field": "has_mailing_address"
},
{
"field": "do_not_contact",
"not": true
},
{
"field": "tags",
"match": "none",
"tag_ids": [
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7a02"
]
}
]
},
"as_of": "2026-10-13",
"member_count": 112,
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"api_key_id": null,
"created_at": "2026-10-13T15:00:00Z"
}
}household
household.created
data is the full household as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e07",
"type": "household.created",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d04",
"name": "The Nakamura household",
"address_line1": "1420 Spruce St",
"address_line2": null,
"city": "Denver",
"region": "CO",
"postal_code": "80205",
"country": "US",
"created_at": "2026-03-01T15:00:00Z",
"updated_at": "2026-03-01T15:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}household.updated
data is the full household as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e08",
"type": "household.updated",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d04",
"name": "The Nakamura household",
"address_line1": "1420 Spruce St",
"address_line2": null,
"city": "Denver",
"region": "CO",
"postal_code": "80205",
"country": "US",
"created_at": "2026-03-01T15:00:00Z",
"updated_at": "2026-03-01T15:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}household.deleted
data is the row with deleted_at set (soft delete, ADR-005). A restore emits household.updated with deleted_at null.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e09",
"type": "household.deleted",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d04",
"name": "The Nakamura household",
"address_line1": "1420 Spruce St",
"address_line2": null,
"city": "Denver",
"region": "CO",
"postal_code": "80205",
"country": "US",
"created_at": "2026-03-01T15:00:00Z",
"updated_at": "2026-10-05T16:00:00Z",
"deleted_at": "2026-10-05T16:00:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02"
}
}contact relationship
contact_relationship.created
data is the full contact relationship as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e10",
"type": "contact_relationship.created",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d23",
"from_contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"to_contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d06",
"relationship": "spouse",
"created_at": "2026-03-01T15:05:00Z",
"updated_at": "2026-03-01T15:05:00Z",
"deleted_at": null,
"deleted_by": null
}
}contact_relationship.updated
data is the full contact relationship as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e11",
"type": "contact_relationship.updated",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d23",
"from_contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"to_contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d06",
"relationship": "spouse",
"created_at": "2026-03-01T15:05:00Z",
"updated_at": "2026-03-01T15:05:00Z",
"deleted_at": null,
"deleted_by": null
}
}contact_relationship.deleted
data is the row with deleted_at set (soft delete, ADR-005). A restore emits contact_relationship.updated with deleted_at null.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e12",
"type": "contact_relationship.deleted",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d23",
"from_contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"to_contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d06",
"relationship": "spouse",
"created_at": "2026-03-01T15:05:00Z",
"updated_at": "2026-10-05T16:00:00Z",
"deleted_at": "2026-10-05T16:00:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02"
}
}fund
fund.created
data is the full fund as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e13",
"type": "fund.created",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"name": "Winter Shelter Fund",
"code": "SHELTER",
"restriction": "purpose",
"purpose": "Overnight shelter operations from November through March",
"restriction_release_date": null,
"is_default": false,
"active": true,
"created_at": "2026-09-01T16:00:00Z",
"updated_at": "2026-09-01T16:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}fund.updated
data is the full fund as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e14",
"type": "fund.updated",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"name": "Winter Shelter Fund",
"code": "SHELTER",
"restriction": "purpose",
"purpose": "Overnight shelter operations from November through March",
"restriction_release_date": null,
"is_default": false,
"active": true,
"created_at": "2026-09-01T16:00:00Z",
"updated_at": "2026-09-01T16:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}fund.deleted
data is the row with deleted_at set (soft delete, ADR-005). A restore emits fund.updated with deleted_at null.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e15",
"type": "fund.deleted",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"name": "Winter Shelter Fund",
"code": "SHELTER",
"restriction": "purpose",
"purpose": "Overnight shelter operations from November through March",
"restriction_release_date": null,
"is_default": false,
"active": true,
"created_at": "2026-09-01T16:00:00Z",
"updated_at": "2026-10-05T16:00:00Z",
"deleted_at": "2026-10-05T16:00:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02"
}
}campaign
campaign.created
data is the full campaign as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e16",
"type": "campaign.created",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d10",
"name": "Winter 2026",
"goal_cents": 5000000,
"starts_on": "2026-10-01",
"ends_on": "2026-12-31",
"default_fund_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"created_at": "2026-09-15T16:00:00Z",
"updated_at": "2026-09-15T16:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}campaign.updated
data is the full campaign as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e17",
"type": "campaign.updated",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d10",
"name": "Winter 2026",
"goal_cents": 5000000,
"starts_on": "2026-10-01",
"ends_on": "2026-12-31",
"default_fund_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"created_at": "2026-09-15T16:00:00Z",
"updated_at": "2026-09-15T16:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}campaign.deleted
data is the row with deleted_at set (soft delete, ADR-005). A restore emits campaign.updated with deleted_at null.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e18",
"type": "campaign.deleted",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d10",
"name": "Winter 2026",
"goal_cents": 5000000,
"starts_on": "2026-10-01",
"ends_on": "2026-12-31",
"default_fund_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"created_at": "2026-09-15T16:00:00Z",
"updated_at": "2026-10-05T16:00:00Z",
"deleted_at": "2026-10-05T16:00:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02"
}
}appeal
appeal.created
data is the full appeal as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e19",
"type": "appeal.created",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d11",
"campaign_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d10",
"name": "September letter",
"channel": "letter",
"sent_on": "2026-09-28",
"cost_cents": 84000,
"created_at": "2026-09-15T17:00:00Z",
"updated_at": "2026-09-28T16:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}appeal.updated
data is the full appeal as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e20",
"type": "appeal.updated",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d11",
"campaign_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d10",
"name": "September letter",
"channel": "letter",
"sent_on": "2026-09-28",
"cost_cents": 84000,
"created_at": "2026-09-15T17:00:00Z",
"updated_at": "2026-09-28T16:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}appeal.deleted
data is the row with deleted_at set (soft delete, ADR-005). A restore emits appeal.updated with deleted_at null.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e21",
"type": "appeal.deleted",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d11",
"campaign_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d10",
"name": "September letter",
"channel": "letter",
"sent_on": "2026-09-28",
"cost_cents": 84000,
"created_at": "2026-09-15T17:00:00Z",
"updated_at": "2026-10-05T16:00:00Z",
"deleted_at": "2026-10-05T16:00:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02"
}
}soft credit
soft_credit.created
data is the full soft credit as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e22",
"type": "soft_credit.created",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d15",
"gift_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d13",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d06",
"contact_name": "Kenji Nakamura",
"amount_cents": 5000,
"role": "household_member",
"reason": null,
"created_at": "2026-10-04T22:16:00Z",
"updated_at": "2026-10-04T22:16:00Z",
"deleted_at": null,
"deleted_by": null
}
}soft_credit.updated
data is the full soft credit as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e23",
"type": "soft_credit.updated",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d15",
"gift_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d13",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d06",
"contact_name": "Kenji Nakamura",
"amount_cents": 5000,
"role": "household_member",
"reason": null,
"created_at": "2026-10-04T22:16:00Z",
"updated_at": "2026-10-04T22:16:00Z",
"deleted_at": null,
"deleted_by": null
}
}soft_credit.deleted
data is the row with deleted_at set (soft delete, ADR-005). A restore emits soft_credit.updated with deleted_at null.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e24",
"type": "soft_credit.deleted",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d15",
"gift_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d13",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d06",
"contact_name": "Kenji Nakamura",
"amount_cents": 5000,
"role": "household_member",
"reason": null,
"created_at": "2026-10-04T22:16:00Z",
"updated_at": "2026-10-05T16:00:00Z",
"deleted_at": "2026-10-05T16:00:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02"
}
}grant
grant.created
data is the full grant as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e25",
"type": "grant.created",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d12",
"funder_contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d07",
"fund_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"name": "Cedar Ridge winter shelter grant 2026",
"status": "awarded",
"awarded_cents": 2500000,
"indirect_rate_bps": 1500,
"period_start": "2026-11-01",
"period_end": "2027-03-31",
"allowed_uses": "Shelter staffing, utilities and bedding. No capital purchases.",
"report_due_on": "2027-04-30",
"next_payment_due_on": "2027-01-15",
"notes": "Second payment on receipt of the interim report.",
"created_at": "2026-09-20T16:00:00Z",
"updated_at": "2026-10-01T16:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}grant.updated
data is the full grant as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e26",
"type": "grant.updated",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d12",
"funder_contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d07",
"fund_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"name": "Cedar Ridge winter shelter grant 2026",
"status": "awarded",
"awarded_cents": 2500000,
"indirect_rate_bps": 1500,
"period_start": "2026-11-01",
"period_end": "2027-03-31",
"allowed_uses": "Shelter staffing, utilities and bedding. No capital purchases.",
"report_due_on": "2027-04-30",
"next_payment_due_on": "2027-01-15",
"notes": "Second payment on receipt of the interim report.",
"created_at": "2026-09-20T16:00:00Z",
"updated_at": "2026-10-01T16:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}grant.deleted
data is the row with deleted_at set (soft delete, ADR-005). A restore emits grant.updated with deleted_at null.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e27",
"type": "grant.deleted",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d12",
"funder_contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d07",
"fund_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"name": "Cedar Ridge winter shelter grant 2026",
"status": "awarded",
"awarded_cents": 2500000,
"indirect_rate_bps": 1500,
"period_start": "2026-11-01",
"period_end": "2027-03-31",
"allowed_uses": "Shelter staffing, utilities and bedding. No capital purchases.",
"report_due_on": "2027-04-30",
"next_payment_due_on": "2027-01-15",
"notes": "Second payment on receipt of the interim report.",
"created_at": "2026-09-20T16:00:00Z",
"updated_at": "2026-10-05T16:00:00Z",
"deleted_at": "2026-10-05T16:00:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02"
}
}communication
communication.created
data is the full communication as returned by GET. Also emitted when an email this system sent is accepted by Postmark (source: system, email_message_id set) and when a BCC-captured email is filed to a contact (source: postmark_inbound, inbound_email_id set), once per contact (ADR-014).
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e28",
"type": "communication.created",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d18",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"channel": "email",
"direction": "outbound",
"subject": "Thank you for keeping the shelter warm",
"body_text": "Aiko, your gift of $50.00 to the Winter Shelter Fund means two more nights of warm beds. Thank you.",
"body_html": null,
"message_id": "<thanks-6d13@maplestreetfoodbank.org>",
"thread_id": null,
"from_address": "luis@maplestreetfoodbank.org",
"to_addresses": [
"aiko.nakamura@example.com"
],
"occurred_at": "2026-10-05T15:00:00Z",
"source": "manual",
"logged_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"inbound_email_id": null,
"email_message_id": null,
"attachment_count": null,
"created_at": "2026-10-05T15:00:00Z",
"updated_at": "2026-10-05T15:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}communication.updated
data is the full communication as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e29",
"type": "communication.updated",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d18",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"channel": "email",
"direction": "outbound",
"subject": "Thank you for keeping the shelter warm",
"body_text": "Aiko, your gift of $50.00 to the Winter Shelter Fund means two more nights of warm beds. Thank you.",
"body_html": null,
"message_id": "<thanks-6d13@maplestreetfoodbank.org>",
"thread_id": null,
"from_address": "luis@maplestreetfoodbank.org",
"to_addresses": [
"aiko.nakamura@example.com"
],
"occurred_at": "2026-10-05T15:00:00Z",
"source": "manual",
"logged_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"inbound_email_id": null,
"email_message_id": null,
"attachment_count": null,
"created_at": "2026-10-05T15:00:00Z",
"updated_at": "2026-10-05T15:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}communication.deleted
data is the row with deleted_at set (soft delete, ADR-005). A restore emits communication.updated with deleted_at null.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e30",
"type": "communication.deleted",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d18",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"channel": "email",
"direction": "outbound",
"subject": "Thank you for keeping the shelter warm",
"body_text": "Aiko, your gift of $50.00 to the Winter Shelter Fund means two more nights of warm beds. Thank you.",
"body_html": null,
"message_id": "<thanks-6d13@maplestreetfoodbank.org>",
"thread_id": null,
"from_address": "luis@maplestreetfoodbank.org",
"to_addresses": [
"aiko.nakamura@example.com"
],
"occurred_at": "2026-10-05T15:00:00Z",
"source": "manual",
"logged_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"inbound_email_id": null,
"email_message_id": null,
"attachment_count": null,
"created_at": "2026-10-05T15:00:00Z",
"updated_at": "2026-10-05T16:00:00Z",
"deleted_at": "2026-10-05T16:00:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02"
}
}task
task.created
data is the full task as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e31",
"type": "task.created",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d19",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"grant_id": null,
"kind": "thank_you",
"title": "Thank Aiko Nakamura for her $50.00 gift",
"due_on": "2026-10-06",
"status": "open",
"assigned_to": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"generated": true,
"snoozed_until": null,
"completed_at": null,
"created_at": "2026-10-04T22:15:00Z",
"updated_at": "2026-10-04T22:15:00Z",
"deleted_at": null,
"deleted_by": null
}
}task.updated
data is the full task as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e32",
"type": "task.updated",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d19",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"grant_id": null,
"kind": "thank_you",
"title": "Thank Aiko Nakamura for her $50.00 gift",
"due_on": "2026-10-06",
"status": "open",
"assigned_to": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"generated": true,
"snoozed_until": null,
"completed_at": null,
"created_at": "2026-10-04T22:15:00Z",
"updated_at": "2026-10-04T22:15:00Z",
"deleted_at": null,
"deleted_by": null
}
}task.deleted
data is the row with deleted_at set (soft delete, ADR-005). A restore emits task.updated with deleted_at null.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e33",
"type": "task.deleted",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d19",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"grant_id": null,
"kind": "thank_you",
"title": "Thank Aiko Nakamura for her $50.00 gift",
"due_on": "2026-10-06",
"status": "open",
"assigned_to": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"generated": true,
"snoozed_until": null,
"completed_at": null,
"created_at": "2026-10-04T22:15:00Z",
"updated_at": "2026-10-05T16:00:00Z",
"deleted_at": "2026-10-05T16:00:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02"
}
}gift
gift.created
data is the full gift as returned by GET. Gifts paid through a donation form (ADR-013) arrive once Stripe reports the money paid, with donation_form_id (and recurring_donation_id for each charge of a recurring donation) set and created_by null.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e34",
"type": "gift.created",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d13",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"contact_name": "Aiko Nakamura",
"fund_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"fund_name": "Winter Shelter Fund",
"campaign_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d10",
"campaign_name": "Winter 2026",
"appeal_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d11",
"appeal_name": "September letter",
"grant_id": null,
"kind": "one_time",
"status": "posted",
"amount_cents": 5000,
"fair_market_value_cents": 0,
"fee_covered_cents": 0,
"received_on": "2026-10-04",
"contribution_date": "2026-10-04",
"payment_method": "card",
"external_ref": "pi_3Q8kLm2eZvKYlo2C1aBcDeFg",
"in_kind_description": null,
"property_description": null,
"book_fair_value_cents": null,
"valuation_method": null,
"nonfinancial_category": null,
"pledge_id": null,
"tribute_type": null,
"tribute_name": null,
"notify_contact_id": null,
"notes": "Gave through the website after the September letter.",
"receipt_required": false,
"donation_form_id": null,
"donation_form_title": null,
"recurring_donation_id": null,
"recurring_interval": null,
"batch_id": null,
"batch_name": null,
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"created_at": "2026-10-04T22:15:00Z",
"updated_at": "2026-10-04T22:15:00Z"
}
}gift.updated
Sent when an in-kind gift's valuation or printed description changes (PATCH /v1/gifts/{id}/valuation). Status changes from a void or full refund arrive as gift_adjustment.created. data is the full gift as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e40",
"type": "gift.updated",
"created_at": "2026-11-20T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d23",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d07",
"fund_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d08",
"kind": "in_kind",
"status": "posted",
"amount_cents": null,
"book_fair_value_cents": 42000,
"valuation_method": "independent appraisal"
}
}gift adjustment
gift_adjustment.created
data is the full gift adjustment as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e35",
"type": "gift_adjustment.created",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d14",
"gift_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d13",
"kind": "reallocate",
"amount_cents": 5000,
"from_fund_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"to_fund_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d08",
"effective_on": "2026-10-05",
"reason": "Donor asked that the gift be unrestricted.",
"exception_reason": null,
"consent_reference": "Donor email of 2026-10-03, filed in the donor record",
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"created_at": "2026-10-05T14:30:00Z",
"updated_at": "2026-10-05T14:30:00Z"
}
}gift batch
gift_batch.created
A deposit batch was started (ADR-019). data is the batch as returned by GET. Gifts entered into it arrive as gift.created with batch_id; they emit no batch event.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e51",
"type": "gift_batch.created",
"created_at": "2026-10-12T16:00:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d71",
"name": "Sunday collection, Oct 12",
"deposit_date": "2026-10-12",
"expected_count": 3,
"expected_total_cents": 22500,
"entered_count": 0,
"entered_total_cents": 0,
"count_difference": 3,
"total_difference_cents": 22500,
"balanced": false,
"status": "open",
"notes": "Plate and envelopes from the 9:30 and 11:00 services.",
"closed_at": null,
"closed_by": null,
"reopened_at": null,
"reopened_by": null,
"reopen_reason": null,
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"created_at": "2026-10-12T16:00:00Z",
"updated_at": "2026-10-12T16:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}gift_batch.updated
An open batch's name, deposit date, expected count or total, or notes changed, or a deleted batch was restored (ADR-001, ADR-005: a restore emits .updated). Closing, reopening and deleting have their own events. data is the batch as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e52",
"type": "gift_batch.updated",
"created_at": "2026-10-12T16:06:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d71",
"name": "Sunday collection, Oct 12",
"deposit_date": "2026-10-12",
"expected_count": 3,
"expected_total_cents": 22500,
"entered_count": 2,
"entered_total_cents": 15000,
"count_difference": 1,
"total_difference_cents": 7500,
"balanced": false,
"status": "open",
"notes": "Plate and envelopes from the 9:30 and 11:00 services.",
"closed_at": null,
"closed_by": null,
"reopened_at": null,
"reopened_by": null,
"reopen_reason": null,
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"created_at": "2026-10-12T16:00:00Z",
"updated_at": "2026-10-12T16:06:00Z",
"deleted_at": null,
"deleted_by": null
}
}gift_batch.deleted
An empty batch was soft-deleted. data is the batch with deleted_at set.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e53",
"type": "gift_batch.deleted",
"created_at": "2026-10-12T15:10:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d72",
"name": "Bake sale cash",
"deposit_date": "2026-10-12",
"expected_count": 1,
"expected_total_cents": 4000,
"entered_count": 0,
"entered_total_cents": 0,
"count_difference": 1,
"total_difference_cents": 4000,
"balanced": false,
"status": "open",
"notes": null,
"closed_at": null,
"closed_by": null,
"reopened_at": null,
"reopened_by": null,
"reopen_reason": null,
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"created_at": "2026-10-12T15:00:00Z",
"updated_at": "2026-10-12T15:10:00Z",
"deleted_at": "2026-10-12T15:10:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03"
}
}gift_batch.closed
A batch closed because it balanced. data is the batch as returned by GET, with closed_at set; its figures no longer change unless an admin reopens it.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e55",
"type": "gift_batch.closed",
"created_at": "2026-10-12T16:20:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d71",
"name": "Sunday collection, Oct 12",
"deposit_date": "2026-10-12",
"expected_count": 3,
"expected_total_cents": 22500,
"entered_count": 3,
"entered_total_cents": 22500,
"count_difference": 0,
"total_difference_cents": 0,
"balanced": true,
"status": "closed",
"notes": "Plate and envelopes from the 9:30 and 11:00 services.",
"closed_at": "2026-10-12T16:20:00Z",
"closed_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"reopened_at": null,
"reopened_by": null,
"reopen_reason": null,
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"created_at": "2026-10-12T16:00:00Z",
"updated_at": "2026-10-12T16:20:00Z",
"deleted_at": null,
"deleted_by": null
}
}gift_batch.reopened
An admin reopened a closed batch. data is the batch as returned by GET, with reopened_at, reopened_by and reopen_reason set.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e56",
"type": "gift_batch.reopened",
"created_at": "2026-10-13T15:00:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d71",
"name": "Sunday collection, Oct 12",
"deposit_date": "2026-10-12",
"expected_count": 3,
"expected_total_cents": 22500,
"entered_count": 3,
"entered_total_cents": 22500,
"count_difference": 0,
"total_difference_cents": 0,
"balanced": true,
"status": "open",
"notes": "Plate and envelopes from the 9:30 and 11:00 services.",
"closed_at": null,
"closed_by": null,
"reopened_at": "2026-10-13T15:00:00Z",
"reopened_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"reopen_reason": "The bank's deposit receipt shows four checks; one was left out of the batch.",
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"created_at": "2026-10-12T16:00:00Z",
"updated_at": "2026-10-13T15:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}receipt
receipt.issued
data is the full receipt as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e36",
"type": "receipt.issued",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d16",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"kind": "gift",
"gift_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d13",
"tax_year": null,
"contribution_date": "2026-10-04",
"total_cents": 5000,
"deductible_cents": 5000,
"goods_services_statement": "No goods or services were provided in exchange for this contribution.",
"rendered_text": "Maple Street Food Bank\n1200 Maple Street\nDenver, CO 80202\nEIN 84-1234567\n\nContribution receipt\n\nReceipt number: 3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d16\nDate issued: October 4, 2026\n\nDonor\nAiko Nakamura\n\nContribution\nContribution date: October 4, 2026\nAmount: $50.00\nDesignated to: General fund (unrestricted)\nDeductible amount: $50.00\n\nNo goods or services were provided in exchange for this contribution.\n\nThis receipt is your written acknowledgment of this contribution for federal income tax purposes. Please keep it with your tax records.\n",
"supersedes_receipt_id": null,
"superseded_at": null,
"reissue_reason": null,
"sent_at": "2026-10-04T22:20:00Z",
"sent_via": "email",
"created_at": "2026-10-04T22:20:00Z",
"updated_at": "2026-10-04T22:20:00Z"
}
}receipt.sent
Postmark accepted a receipt for delivery to its donor (C2, ADR-014): the receipt's sent_at and sent_via are set. Emitted for every send, a resend included. data is the full receipt as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e46",
"type": "receipt.sent",
"created_at": "2026-10-04T22:20:41Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d16",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"kind": "gift",
"gift_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d13",
"tax_year": null,
"contribution_date": "2026-10-04",
"total_cents": 5000,
"deductible_cents": 5000,
"goods_services_statement": "No goods or services were provided in exchange for this contribution.",
"rendered_text": "Maple Street Food Bank\n1200 Maple Street\nDenver, CO 80202\nEIN 84-1234567\n\nContribution receipt\n\nReceipt number: 3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d16\nDate issued: October 4, 2026\n\nDonor\nAiko Nakamura\n\nContribution\nContribution date: October 4, 2026\nAmount: $50.00\nDesignated to: General fund (unrestricted)\nDeductible amount: $50.00\n\nNo goods or services were provided in exchange for this contribution.\n\nThis receipt is your written acknowledgment of this contribution for federal income tax purposes. Please keep it with your tax records.\n",
"supersedes_receipt_id": null,
"superseded_at": null,
"reissue_reason": null,
"sent_at": "2026-10-04T22:20:00Z",
"sent_via": "email",
"created_at": "2026-10-04T22:20:00Z",
"updated_at": "2026-10-04T22:20:00Z"
}
}import job
import_job.committed
data is the import job with created_ids; one contact.created or gift.created follows per record.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e37",
"type": "import_job.committed",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d20",
"source_name": "donors-2025.csv",
"status": "committed",
"column_mapping": {
"First Name": "contact.first_name",
"Last Name": "contact.last_name",
"Email": "contact.email",
"Amount": "gift.amount",
"Date": "gift.received_on",
"Fund": "gift.fund"
},
"row_count": 412,
"error_count": 3,
"created_ids": {
"contacts": [
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d31"
],
"gifts": [
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d32"
]
},
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"created_at": "2026-10-05T13:00:00Z",
"updated_at": "2026-10-05T13:10:00Z",
"discarded_at": null,
"discarded_by": null,
"discard_reason": null
}
}import_job.rolled_back
data is the import job; its gifts were voided and its contacts removed.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e38",
"type": "import_job.rolled_back",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d20",
"source_name": "donors-2025.csv",
"status": "committed",
"column_mapping": {
"First Name": "contact.first_name",
"Last Name": "contact.last_name",
"Email": "contact.email",
"Amount": "gift.amount",
"Date": "gift.received_on",
"Fund": "gift.fund"
},
"row_count": 412,
"error_count": 3,
"created_ids": {
"contacts": [
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d31"
],
"gifts": [
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d32"
]
},
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"created_at": "2026-10-05T13:00:00Z",
"updated_at": "2026-10-05T13:10:00Z",
"discarded_at": null,
"discarded_by": null,
"discard_reason": null
}
}import_job.discarded
An import that was never imported was discarded, when someone asked (discard_reason: requested) or 30 days after its last change (expired), and its rows were cleared (E7 fix round, ADR-007). data is ImportJobDiscardedEvent: the import's id, status, reason and time only, never its file name or rows.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e39",
"type": "import_job.discarded",
"created_at": "2026-10-05T13:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d21",
"status": "discarded",
"discard_reason": "requested",
"discarded_at": "2026-10-05T13:30:00Z"
}
}donation form
donation_form.created
data is the full donation form as returned by GET (ADR-013).
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e41",
"type": "donation_form.created",
"created_at": "2026-10-01T16:00:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d51",
"slug": "winter-shelter",
"title": "Keep the shelter warm this winter",
"intro": "Every $35 gives one guest a warm bed, dinner and breakfast.",
"logo_url": "https://maplestreetfoodbank.org/logo.png",
"accent_color": "#1f6f4a",
"preset_amounts_cents": [
2500,
5000,
10000,
25000
],
"default_amount_cents": 5000,
"allow_custom_amount": true,
"min_amount_cents": 500,
"max_amount_cents": 2500000,
"frequencies": [
"one_time",
"month"
],
"fund_ids": [
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d08"
],
"campaign_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d10",
"appeal_id": null,
"thank_you_message": "Thank you for keeping the shelter warm.",
"allow_tributes": true,
"active": true,
"created_at": "2026-10-01T16:00:00Z",
"updated_at": "2026-10-02T16:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}donation_form.updated
data is the full donation form as returned by GET. Also sent for every active form when Stripe stops allowing charges on the organization's account and the form is turned off (active: false).
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e42",
"type": "donation_form.updated",
"created_at": "2026-10-02T16:00:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d51",
"slug": "winter-shelter",
"title": "Keep the shelter warm this winter",
"intro": "Every $35 gives one guest a warm bed, dinner and breakfast.",
"logo_url": "https://maplestreetfoodbank.org/logo.png",
"accent_color": "#1f6f4a",
"preset_amounts_cents": [
2500,
5000,
10000,
25000
],
"default_amount_cents": 5000,
"allow_custom_amount": true,
"min_amount_cents": 500,
"max_amount_cents": 2500000,
"frequencies": [
"one_time",
"month"
],
"fund_ids": [
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d08"
],
"campaign_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d10",
"appeal_id": null,
"thank_you_message": "Thank you for keeping the shelter warm.",
"allow_tributes": true,
"active": true,
"created_at": "2026-10-01T16:00:00Z",
"updated_at": "2026-10-02T16:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}donation_form.deleted
data is the row with deleted_at set (soft delete, ADR-005). A restore emits donation_form.updated with deleted_at null.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e43",
"type": "donation_form.deleted",
"created_at": "2026-10-05T16:00:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d51",
"slug": "winter-shelter",
"title": "Keep the shelter warm this winter",
"intro": "Every $35 gives one guest a warm bed, dinner and breakfast.",
"logo_url": "https://maplestreetfoodbank.org/logo.png",
"accent_color": "#1f6f4a",
"preset_amounts_cents": [
2500,
5000,
10000,
25000
],
"default_amount_cents": 5000,
"allow_custom_amount": true,
"min_amount_cents": 500,
"max_amount_cents": 2500000,
"frequencies": [
"one_time",
"month"
],
"fund_ids": [
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d08"
],
"campaign_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d10",
"appeal_id": null,
"thank_you_message": "Thank you for keeping the shelter warm.",
"allow_tributes": true,
"active": true,
"created_at": "2026-10-01T16:00:00Z",
"updated_at": "2026-10-05T16:00:00Z",
"deleted_at": "2026-10-05T16:00:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02"
}
}recurring donation
recurring_donation.created
Sent when a donor starts a monthly or yearly gift on a donation form (ADR-013). data is the full recurring donation as returned by GET. Each paid charge then arrives as gift.created with its recurring_donation_id.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e44",
"type": "recurring_donation.created",
"created_at": "2026-10-04T22:15:03Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d52",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"contact_name": "Aiko Nakamura",
"fund_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"fund_name": "Winter Shelter Fund",
"campaign_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d10",
"appeal_id": null,
"donation_form_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d51",
"amount_cents": 2500,
"interval": "month",
"status": "active",
"cancel_at_period_end": false,
"current_period_end": "2026-12-04T22:15:00Z",
"started_at": "2026-10-04T22:15:00Z",
"canceled_at": null,
"stripe_subscription_id": "sub_1QAbCd2eZvKYlo2CfGhIjKlM",
"stripe_customer_id": "cus_QAbCdEfGhIjKlM",
"tribute_type": "in_memory_of",
"tribute_name": "Grace Okafor",
"created_at": "2026-10-04T22:15:03Z",
"updated_at": "2026-11-04T22:15:04Z"
}
}recurring_donation.updated
Sent on cancel and resume, and when Stripe reports a change: past due, canceled, a new period end, amount or interval. data is the full recurring donation as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e45",
"type": "recurring_donation.updated",
"created_at": "2026-11-04T22:15:04Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d52",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"contact_name": "Aiko Nakamura",
"fund_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d09",
"fund_name": "Winter Shelter Fund",
"campaign_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d10",
"appeal_id": null,
"donation_form_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d51",
"amount_cents": 2500,
"interval": "month",
"status": "active",
"cancel_at_period_end": false,
"current_period_end": "2026-12-04T22:15:00Z",
"started_at": "2026-10-04T22:15:00Z",
"canceled_at": null,
"stripe_subscription_id": "sub_1QAbCd2eZvKYlo2CfGhIjKlM",
"stripe_customer_id": "cus_QAbCdEfGhIjKlM",
"tribute_type": "in_memory_of",
"tribute_name": "Grace Okafor",
"created_at": "2026-10-04T22:15:03Z",
"updated_at": "2026-11-04T22:15:04Z"
}
}welcome enrollment
welcome_enrollment.created
A new donor was enrolled in the welcome series (ADR-015). data is the enrolment as returned by GET.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e47",
"type": "welcome_enrollment.created",
"created_at": "2026-10-05T14:45:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d70",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"first_gift_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d13",
"status": "active",
"next_step": "impact_story",
"next_step_at": "2026-10-19T16:00:00Z",
"stop_reason": null,
"enrolled_at": "2026-10-05T14:45:00Z",
"completed_at": null,
"stopped_at": null,
"steps": [
{
"step": "thank_you",
"outcome": "queued",
"reason": null,
"email_message_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d71",
"email_status": "sent",
"email_sent_at": "2026-10-05T14:45:41Z",
"email_error": null,
"created_at": "2026-10-05T14:45:00Z"
}
],
"created_at": "2026-10-05T14:45:00Z",
"updated_at": "2026-10-05T14:45:00Z"
}
}welcome_enrollment.updated
An enrolment moved on: a step was queued or skipped, it completed, or it was stopped (by staff or a check at send time) or resumed. data is the enrolment as returned by GET, with its step outcomes.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e48",
"type": "welcome_enrollment.updated",
"created_at": "2026-10-05T14:45:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d70",
"contact_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d05",
"first_gift_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d13",
"status": "active",
"next_step": "impact_story",
"next_step_at": "2026-10-19T16:00:00Z",
"stop_reason": null,
"enrolled_at": "2026-10-05T14:45:00Z",
"completed_at": null,
"stopped_at": null,
"steps": [
{
"step": "thank_you",
"outcome": "queued",
"reason": null,
"email_message_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d71",
"email_status": "sent",
"email_sent_at": "2026-10-05T14:45:41Z",
"email_error": null,
"created_at": "2026-10-05T14:45:00Z"
}
],
"created_at": "2026-10-05T14:45:00Z",
"updated_at": "2026-10-05T14:45:00Z"
}
}email template
email_template.created
data is the template as GET returns it.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c8e01",
"type": "email_template.created",
"created_at": "2026-10-13T14:05:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c8a01",
"name": "Spring newsletter",
"content": {
"subject": [
{
"type": "merge",
"field": "first_name"
},
{
"type": "text",
"text": ", spring at the pantry"
}
],
"preheader": [
{
"type": "text",
"text": "What your gifts did this winter"
}
],
"blocks": [
{
"type": "heading",
"runs": [
{
"type": "text",
"text": "Spring at the pantry"
}
]
},
{
"type": "text",
"runs": [
{
"type": "text",
"text": "Thank you for "
},
{
"type": "merge",
"field": "last_gift_amount",
"fallback": "your support"
},
{
"type": "text",
"text": ". Read "
},
{
"type": "text",
"text": "the whole story",
"link": "https://maplestreetfoodbank.org/spring"
},
{
"type": "text",
"text": "."
}
]
},
{
"type": "image",
"src": "https://maplestreetfoodbank.org/img/volunteers.jpg",
"alt": "Volunteers sorting cans",
"width": 600
},
{
"type": "donation_link",
"label": "Give again",
"donation_form_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e10",
"audience": "donors"
}
]
},
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"updated_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"created_at": "2026-10-12T15:00:00Z",
"updated_at": "2026-10-12T15:30:00Z",
"deleted_at": null,
"deleted_by": null
}
}email_template.updated
data is the template as GET returns it, after a change or a restore.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c8e02",
"type": "email_template.updated",
"created_at": "2026-10-13T14:05:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c8a01",
"name": "Spring newsletter",
"content": {
"subject": [
{
"type": "merge",
"field": "first_name"
},
{
"type": "text",
"text": ", spring at the pantry"
}
],
"preheader": [
{
"type": "text",
"text": "What your gifts did this winter"
}
],
"blocks": [
{
"type": "heading",
"runs": [
{
"type": "text",
"text": "Spring at the pantry"
}
]
},
{
"type": "text",
"runs": [
{
"type": "text",
"text": "Thank you for "
},
{
"type": "merge",
"field": "last_gift_amount",
"fallback": "your support"
},
{
"type": "text",
"text": ". Read "
},
{
"type": "text",
"text": "the whole story",
"link": "https://maplestreetfoodbank.org/spring"
},
{
"type": "text",
"text": "."
}
]
},
{
"type": "image",
"src": "https://maplestreetfoodbank.org/img/volunteers.jpg",
"alt": "Volunteers sorting cans",
"width": 600
},
{
"type": "donation_link",
"label": "Give again",
"donation_form_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e10",
"audience": "donors"
}
]
},
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"updated_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"created_at": "2026-10-12T15:00:00Z",
"updated_at": "2026-10-12T15:30:00Z",
"deleted_at": null,
"deleted_by": null
}
}email_template.deleted
data is the template as GET returns it, deleted_at set.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c8e03",
"type": "email_template.deleted",
"created_at": "2026-10-13T14:05:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c8a01",
"name": "Spring newsletter",
"content": {
"subject": [
{
"type": "merge",
"field": "first_name"
},
{
"type": "text",
"text": ", spring at the pantry"
}
],
"preheader": [
{
"type": "text",
"text": "What your gifts did this winter"
}
],
"blocks": [
{
"type": "heading",
"runs": [
{
"type": "text",
"text": "Spring at the pantry"
}
]
},
{
"type": "text",
"runs": [
{
"type": "text",
"text": "Thank you for "
},
{
"type": "merge",
"field": "last_gift_amount",
"fallback": "your support"
},
{
"type": "text",
"text": ". Read "
},
{
"type": "text",
"text": "the whole story",
"link": "https://maplestreetfoodbank.org/spring"
},
{
"type": "text",
"text": "."
}
]
},
{
"type": "image",
"src": "https://maplestreetfoodbank.org/img/volunteers.jpg",
"alt": "Volunteers sorting cans",
"width": 600
},
{
"type": "donation_link",
"label": "Give again",
"donation_form_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e10",
"audience": "donors"
}
]
},
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"updated_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"created_at": "2026-10-12T15:00:00Z",
"updated_at": "2026-10-12T15:30:00Z",
"deleted_at": "2026-10-13T09:00:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02"
}
}email send
email_send.created
data is the send as GET returns it.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c8e04",
"type": "email_send.created",
"created_at": "2026-10-13T14:05:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c8b01",
"name": "Spring newsletter 2026",
"topic": "newsletter",
"content": {
"subject": [
{
"type": "merge",
"field": "first_name"
},
{
"type": "text",
"text": ", spring at the pantry"
}
],
"preheader": [
{
"type": "text",
"text": "What your gifts did this winter"
}
],
"blocks": [
{
"type": "heading",
"runs": [
{
"type": "text",
"text": "Spring at the pantry"
}
]
},
{
"type": "text",
"runs": [
{
"type": "text",
"text": "Thank you for "
},
{
"type": "merge",
"field": "last_gift_amount",
"fallback": "your support"
},
{
"type": "text",
"text": ". Read "
},
{
"type": "text",
"text": "the whole story",
"link": "https://maplestreetfoodbank.org/spring"
},
{
"type": "text",
"text": "."
}
]
},
{
"type": "image",
"src": "https://maplestreetfoodbank.org/img/volunteers.jpg",
"alt": "Volunteers sorting cans",
"width": 600
},
{
"type": "donation_link",
"label": "Give again",
"donation_form_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e10",
"audience": "donors"
}
]
},
"render_version": 1,
"template_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c8a01",
"saved_list_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7c01",
"snapshot_id": null,
"campaign_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d07",
"appeal_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d08",
"household_option": "per_person",
"track_clicks": false,
"attribution_code": "q4m2x7k3p9ab",
"status": "draft",
"content_hash": null,
"members_hash": null,
"review": null,
"reviewed_at": null,
"reviewed_by": null,
"approved_at": null,
"approved_by": null,
"scheduled_for": null,
"started_at": null,
"completed_at": null,
"paused_at": null,
"pause_reason": null,
"resumed_at": null,
"canceled_at": null,
"canceled_by": null,
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"api_key_id": null,
"created_at": "2026-10-12T16:00:00Z",
"updated_at": "2026-10-12T16:00:00Z",
"deleted_at": null,
"deleted_by": null
}
}email_send.updated
data is the send as GET returns it, after an edit, a review, an approval, a status change (started, paused, resumed, canceled, sent) or a restore. Outbox rows and click counts emit nothing.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c8e05",
"type": "email_send.updated",
"created_at": "2026-10-13T14:05:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c8b01",
"name": "Spring newsletter 2026",
"topic": "newsletter",
"content": {
"subject": [
{
"type": "merge",
"field": "first_name"
},
{
"type": "text",
"text": ", spring at the pantry"
}
],
"preheader": [
{
"type": "text",
"text": "What your gifts did this winter"
}
],
"blocks": [
{
"type": "heading",
"runs": [
{
"type": "text",
"text": "Spring at the pantry"
}
]
},
{
"type": "text",
"runs": [
{
"type": "text",
"text": "Thank you for "
},
{
"type": "merge",
"field": "last_gift_amount",
"fallback": "your support"
},
{
"type": "text",
"text": ". Read "
},
{
"type": "text",
"text": "the whole story",
"link": "https://maplestreetfoodbank.org/spring"
},
{
"type": "text",
"text": "."
}
]
},
{
"type": "image",
"src": "https://maplestreetfoodbank.org/img/volunteers.jpg",
"alt": "Volunteers sorting cans",
"width": 600
},
{
"type": "donation_link",
"label": "Give again",
"donation_form_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e10",
"audience": "donors"
}
]
},
"render_version": 1,
"template_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c8a01",
"saved_list_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7c01",
"snapshot_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7d01",
"campaign_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d07",
"appeal_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d08",
"household_option": "per_person",
"track_clicks": false,
"attribution_code": "q4m2x7k3p9ab",
"status": "scheduled",
"content_hash": "5b2f0a9c4d8e1f3a6b7c9d0e2f4a6b8c0d1e3f5a7b9c2d4e6f8a0b1c3d5e7f90",
"members_hash": "0a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9",
"review": {
"list_name": "Lapsed shelter donors",
"list_version": 3,
"snapshot_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7d01",
"member_count": 1240,
"eligible_count": 1102,
"skipped": [
{
"reason": "not_subscribed",
"words": "Not emailed: the contact hasn't subscribed to email updates.",
"count": 120
},
{
"reason": "no_address",
"words": "Not emailed: the contact has no email address.",
"count": 18
}
],
"subscribed_since_last_send": {
"since": "2026-09-01T15:00:00Z",
"imports": 0,
"staff": 4
},
"address_changed_since_last_send": {
"since": "2026-09-01T15:00:00Z",
"staff": 2,
"api_key": 0,
"import": 0
},
"link_hosts": [
"maplestreetfoodbank.org"
],
"image_hosts": [
"maplestreetfoodbank.org"
],
"sending_domain": "mail.maplestreetfoodbank.org",
"missing_settings": [],
"dmarc_warning": null,
"largest_email_bytes": 5120
},
"reviewed_at": "2026-10-13T14:00:00Z",
"reviewed_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"approved_at": "2026-10-13T14:05:00Z",
"approved_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d03",
"scheduled_for": "2026-10-13T14:15:00Z",
"started_at": null,
"completed_at": null,
"paused_at": null,
"pause_reason": null,
"resumed_at": null,
"canceled_at": null,
"canceled_by": null,
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"api_key_id": null,
"created_at": "2026-10-12T16:00:00Z",
"updated_at": "2026-10-13T14:05:00Z",
"deleted_at": null,
"deleted_by": null
}
}email_send.deleted
data is the draft as GET returns it, deleted_at set.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c8e06",
"type": "email_send.deleted",
"created_at": "2026-10-13T14:05:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c8b01",
"name": "Spring newsletter 2026",
"topic": "newsletter",
"content": {
"subject": [
{
"type": "merge",
"field": "first_name"
},
{
"type": "text",
"text": ", spring at the pantry"
}
],
"preheader": [
{
"type": "text",
"text": "What your gifts did this winter"
}
],
"blocks": [
{
"type": "heading",
"runs": [
{
"type": "text",
"text": "Spring at the pantry"
}
]
},
{
"type": "text",
"runs": [
{
"type": "text",
"text": "Thank you for "
},
{
"type": "merge",
"field": "last_gift_amount",
"fallback": "your support"
},
{
"type": "text",
"text": ". Read "
},
{
"type": "text",
"text": "the whole story",
"link": "https://maplestreetfoodbank.org/spring"
},
{
"type": "text",
"text": "."
}
]
},
{
"type": "image",
"src": "https://maplestreetfoodbank.org/img/volunteers.jpg",
"alt": "Volunteers sorting cans",
"width": 600
},
{
"type": "donation_link",
"label": "Give again",
"donation_form_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e10",
"audience": "donors"
}
]
},
"render_version": 1,
"template_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c8a01",
"saved_list_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c7c01",
"snapshot_id": null,
"campaign_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d07",
"appeal_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d08",
"household_option": "per_person",
"track_clicks": false,
"attribution_code": "q4m2x7k3p9ab",
"status": "draft",
"content_hash": null,
"members_hash": null,
"review": null,
"reviewed_at": null,
"reviewed_by": null,
"approved_at": null,
"approved_by": null,
"scheduled_for": null,
"started_at": null,
"completed_at": null,
"paused_at": null,
"pause_reason": null,
"resumed_at": null,
"canceled_at": null,
"canceled_by": null,
"created_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02",
"api_key_id": null,
"created_at": "2026-10-12T16:00:00Z",
"updated_at": "2026-10-12T16:00:00Z",
"deleted_at": "2026-10-13T09:00:00Z",
"deleted_by": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d02"
}
}webhook
webhook.test
Sent by POST /v1/webhook-subscriptions/{id}/test.
Example body
{
"id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6e39",
"type": "webhook.test",
"created_at": "2026-10-05T14:30:00Z",
"organization_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d01",
"data": {
"subscription_id": "3f9d2c1a-7b4e-4f6a-9c2d-1e5b8a7c6d22",
"message": "This is a test delivery from Operation Charity."
}
}