curl --request POST \
--url https://prod-api.raisedonors.com/api/RecurringGift/query \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"filter": "<string>",
"skip": 123,
"take": 123,
"sortBy": "createdate",
"descending": true,
"includeDetails": true,
"groups": [
{
"conditions": [
{
"parameter": "<string>",
"value": "<string>",
"secondaryValue": "<string>",
"values": [
"<string>"
]
}
]
}
],
"selectedColumns": [
"<string>"
]
}
'const options = {
method: 'POST',
headers: {Authorization: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
filter: '<string>',
skip: 123,
take: 123,
sortBy: 'createdate',
descending: true,
includeDetails: true,
groups: [
{
conditions: [
{
parameter: '<string>',
value: '<string>',
secondaryValue: '<string>',
values: ['<string>']
}
]
}
],
selectedColumns: ['<string>']
})
};
fetch('https://prod-api.raisedonors.com/api/RecurringGift/query', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://prod-api.raisedonors.com/api/RecurringGift/query"
payload = {
"filter": "<string>",
"skip": 123,
"take": 123,
"sortBy": "createdate",
"descending": True,
"includeDetails": True,
"groups": [{ "conditions": [
{
"parameter": "<string>",
"value": "<string>",
"secondaryValue": "<string>",
"values": ["<string>"]
}
] }],
"selectedColumns": ["<string>"]
}
headers = {
"Authorization": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)using RestSharp;
var options = new RestClientOptions("https://prod-api.raisedonors.com/api/RecurringGift/query");
var client = new RestClient(options);
var request = new RestRequest("");
request.AddHeader("Authorization", "<api-key>");
request.AddJsonBody("{\n \"filter\": \"<string>\",\n \"skip\": 123,\n \"take\": 123,\n \"sortBy\": \"createdate\",\n \"descending\": true,\n \"includeDetails\": true,\n \"groups\": [\n {\n \"conditions\": [\n {\n \"parameter\": \"<string>\",\n \"value\": \"<string>\",\n \"secondaryValue\": \"<string>\",\n \"values\": [\n \"<string>\"\n ]\n }\n ]\n }\n ],\n \"selectedColumns\": [\n \"<string>\"\n ]\n}", false);
var response = await client.PostAsync(request);
Console.WriteLine("{0}", response.Content);
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://prod-api.raisedonors.com/api/RecurringGift/query",
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([
'filter' => '<string>',
'skip' => 123,
'take' => 123,
'sortBy' => 'createdate',
'descending' => true,
'includeDetails' => true,
'groups' => [
[
'conditions' => [
[
'parameter' => '<string>',
'value' => '<string>',
'secondaryValue' => '<string>',
'values' => [
'<string>'
]
]
]
]
],
'selectedColumns' => [
'<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}HttpResponse<String> response = Unirest.post("https://prod-api.raisedonors.com/api/RecurringGift/query")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"filter\": \"<string>\",\n \"skip\": 123,\n \"take\": 123,\n \"sortBy\": \"createdate\",\n \"descending\": true,\n \"includeDetails\": true,\n \"groups\": [\n {\n \"conditions\": [\n {\n \"parameter\": \"<string>\",\n \"value\": \"<string>\",\n \"secondaryValue\": \"<string>\",\n \"values\": [\n \"<string>\"\n ]\n }\n ]\n }\n ],\n \"selectedColumns\": [\n \"<string>\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://prod-api.raisedonors.com/api/RecurringGift/query")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"filter\": \"<string>\",\n \"skip\": 123,\n \"take\": 123,\n \"sortBy\": \"createdate\",\n \"descending\": true,\n \"includeDetails\": true,\n \"groups\": [\n {\n \"conditions\": [\n {\n \"parameter\": \"<string>\",\n \"value\": \"<string>\",\n \"secondaryValue\": \"<string>\",\n \"values\": [\n \"<string>\"\n ]\n }\n ]\n }\n ],\n \"selectedColumns\": [\n \"<string>\"\n ]\n}"
response = http.request(request)
puts response.read_bodyconst url = 'https://prod-api.raisedonors.com/api/RecurringGift/query';
const options = {
method: 'POST',
headers: {Authorization: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
filter: '<string>',
skip: 123,
take: 123,
sortBy: 'createdate',
descending: true,
includeDetails: true,
groups: [
{
conditions: [
{
parameter: '<string>',
value: '<string>',
secondaryValue: '<string>',
values: ['<string>']
}
]
}
],
selectedColumns: ['<string>']
})
};
fetch(url, options)
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.error(err));{
"items": [
{
"id": 700089,
"organizationId": 50,
"createdDate": "2023-02-15T10:30:00Z",
"modifiedDate": "2025-11-15T03:00:00Z",
"profileNumber": "RG-700089",
"amount": 50,
"currencyCode": "USD",
"baseCurrencyCode": "USD",
"formattedAmount": "$50.00",
"donor": {
"id": 200456,
"name": "Priya Sharma",
"title": "Ms.",
"firstName": "Priya",
"middleName": null,
"lastName": "Sharma",
"suffix": null,
"email": "priya.sharma@example.org",
"phone": "(555) 012-3450",
"createdDate": "2023-02-15T10:30:00Z",
"modifiedDate": "2025-11-15T03:00:00Z",
"notes": "Monthly donor since February 2023. Volunteer organizer.",
"isOrganization": false,
"organizationName": null,
"crmKeyUrls": {
"VirtuousCRM": {
"crmKey": "100456",
"url": "https://app.virtuoussoftware.com/Generosity/Contact/Detail/100456"
}
},
"crmKey": "100456",
"crmSecondKey": null,
"isTestMode": false,
"isArchived": false,
"isGDPRDeleted": false,
"primaryAddress": {
"id": 400156,
"donorId": 200456,
"isPrimary": true,
"address1": "1542 Elmwood Lane",
"address2": null,
"city": "Springfield",
"state": "OR",
"postal": "97477",
"countryString": "United States",
"addressTypeDisplay": "Home"
},
"donorAddresses": [
{
"id": 400156,
"donorId": 200456,
"isPrimary": true,
"address1": "1542 Elmwood Lane",
"address2": null,
"city": "Springfield",
"state": "OR",
"postal": "97477",
"countryString": "United States",
"addressTypeDisplay": "Home"
}
],
"donorEmailAddresses": [
{
"id": 300156,
"donorId": 200456,
"typeDisplay": "Home Email",
"value": "priya.sharma@example.org",
"secondaryValue": null,
"isPrimary": true,
"isOptedIn": true,
"isValid": true,
"isEmail": true,
"isPhone": false,
"isBounced": false,
"countryCode": null
}
],
"donorPhoneNumbers": [
{
"id": 300157,
"donorId": 200456,
"typeDisplay": "Mobile Phone",
"value": "(555) 012-3450",
"secondaryValue": null,
"isPrimary": true,
"isOptedIn": true,
"isValid": true,
"isEmail": false,
"isPhone": true,
"isBounced": false,
"countryCode": "1"
}
]
},
"formattedFrequency": "Monthly",
"total": 1700,
"formattedTotal": "$1,700.00",
"successfulCycles": 34,
"startDate": "2023-02-15T00:00:00Z",
"startDateFormatted": "February 15, 2023",
"nextChargeDate": "2025-12-15T00:00:00Z",
"nextChargeDateFormatted": "December 15, 2025",
"statusText": "Active",
"gatewayId": 4180,
"gateway": "Riverside Hope — Stripe",
"paymentInfo": "Visa •••• 4242",
"expMonthAndYear": "04/2027",
"cardBrand": "Visa",
"paymentMethodCountryCode": "US",
"projectAllocation": [
{
"projectId": 3010,
"projectName": "General Operating Support",
"amount": 50,
"formattedAmount": "$50.00"
}
],
"isAnonymous": false,
"isTestMode": false,
"donorCoverCosts": false,
"motivationCode": "MGS-WEB",
"sourceCode": "monthly-giving-society",
"segmentId": null,
"segmentName": null,
"segmentCode": null,
"notes": "Monthly Giving Society member since February 2023.",
"comments": null,
"premium": null,
"crmKey": "700089",
"crmKeyUrls": {},
"merchantAccount": "riverside-hope-usd",
"hasPaymentFailed": false,
"isLegacy": false,
"currencyExchange": null,
"tribute": null,
"paymentMethodId": "pm_1QU4cxHs1Yb4Lm7B",
"costs": 1.75
},
{
"id": 700142,
"organizationId": 50,
"createdDate": "2024-04-01T08:12:44Z",
"modifiedDate": "2025-11-01T03:00:00Z",
"profileNumber": "RG-700142",
"amount": 25,
"currencyCode": "USD",
"baseCurrencyCode": "USD",
"formattedAmount": "$25.00",
"formattedFrequency": "Monthly",
"total": 500,
"formattedTotal": "$500.00",
"successfulCycles": 20,
"startDate": "2024-04-01T00:00:00Z",
"startDateFormatted": "April 1, 2024",
"nextChargeDate": "2025-12-01T00:00:00Z",
"nextChargeDateFormatted": "December 1, 2025",
"statusText": "Active",
"gateway": "Riverside Hope — Stripe",
"paymentInfo": "Mastercard •••• 8210",
"expMonthAndYear": "11/2025",
"cardBrand": "Mastercard",
"paymentMethodCountryCode": "US",
"projectAllocation": [
{
"projectId": 3012,
"projectName": "Year-End 2025 — Holiday Meals",
"amount": 25,
"formattedAmount": "$25.00"
}
],
"isAnonymous": false,
"isTestMode": false,
"donorCoverCosts": true,
"notes": null,
"hasPaymentFailed": true,
"isLegacy": false,
"costs": 0.98
}
],
"total": 214
}{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"groups[0].conditions[0].parameter": [
"'donorEmail' is not a valid parameter for this query type. Call Get Query Options for the parameters this query type supports."
]
},
"instance": "/api/RecurringGift/query"
}{
"type": "https://tools.ietf.org/html/rfc7235#section-3.1",
"title": "Unauthorized",
"status": 401,
"detail": "The Authorization header is missing, malformed, or carries a token that is expired or revoked.",
"instance": "/api/RecurringGift/query"
}Query Recurring Gifts
Searches recurring gifts with the query builder and returns a paginated list of matches.
curl --request POST \
--url https://prod-api.raisedonors.com/api/RecurringGift/query \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"filter": "<string>",
"skip": 123,
"take": 123,
"sortBy": "createdate",
"descending": true,
"includeDetails": true,
"groups": [
{
"conditions": [
{
"parameter": "<string>",
"value": "<string>",
"secondaryValue": "<string>",
"values": [
"<string>"
]
}
]
}
],
"selectedColumns": [
"<string>"
]
}
'const options = {
method: 'POST',
headers: {Authorization: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
filter: '<string>',
skip: 123,
take: 123,
sortBy: 'createdate',
descending: true,
includeDetails: true,
groups: [
{
conditions: [
{
parameter: '<string>',
value: '<string>',
secondaryValue: '<string>',
values: ['<string>']
}
]
}
],
selectedColumns: ['<string>']
})
};
fetch('https://prod-api.raisedonors.com/api/RecurringGift/query', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://prod-api.raisedonors.com/api/RecurringGift/query"
payload = {
"filter": "<string>",
"skip": 123,
"take": 123,
"sortBy": "createdate",
"descending": True,
"includeDetails": True,
"groups": [{ "conditions": [
{
"parameter": "<string>",
"value": "<string>",
"secondaryValue": "<string>",
"values": ["<string>"]
}
] }],
"selectedColumns": ["<string>"]
}
headers = {
"Authorization": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)using RestSharp;
var options = new RestClientOptions("https://prod-api.raisedonors.com/api/RecurringGift/query");
var client = new RestClient(options);
var request = new RestRequest("");
request.AddHeader("Authorization", "<api-key>");
request.AddJsonBody("{\n \"filter\": \"<string>\",\n \"skip\": 123,\n \"take\": 123,\n \"sortBy\": \"createdate\",\n \"descending\": true,\n \"includeDetails\": true,\n \"groups\": [\n {\n \"conditions\": [\n {\n \"parameter\": \"<string>\",\n \"value\": \"<string>\",\n \"secondaryValue\": \"<string>\",\n \"values\": [\n \"<string>\"\n ]\n }\n ]\n }\n ],\n \"selectedColumns\": [\n \"<string>\"\n ]\n}", false);
var response = await client.PostAsync(request);
Console.WriteLine("{0}", response.Content);
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://prod-api.raisedonors.com/api/RecurringGift/query",
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([
'filter' => '<string>',
'skip' => 123,
'take' => 123,
'sortBy' => 'createdate',
'descending' => true,
'includeDetails' => true,
'groups' => [
[
'conditions' => [
[
'parameter' => '<string>',
'value' => '<string>',
'secondaryValue' => '<string>',
'values' => [
'<string>'
]
]
]
]
],
'selectedColumns' => [
'<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}HttpResponse<String> response = Unirest.post("https://prod-api.raisedonors.com/api/RecurringGift/query")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"filter\": \"<string>\",\n \"skip\": 123,\n \"take\": 123,\n \"sortBy\": \"createdate\",\n \"descending\": true,\n \"includeDetails\": true,\n \"groups\": [\n {\n \"conditions\": [\n {\n \"parameter\": \"<string>\",\n \"value\": \"<string>\",\n \"secondaryValue\": \"<string>\",\n \"values\": [\n \"<string>\"\n ]\n }\n ]\n }\n ],\n \"selectedColumns\": [\n \"<string>\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://prod-api.raisedonors.com/api/RecurringGift/query")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"filter\": \"<string>\",\n \"skip\": 123,\n \"take\": 123,\n \"sortBy\": \"createdate\",\n \"descending\": true,\n \"includeDetails\": true,\n \"groups\": [\n {\n \"conditions\": [\n {\n \"parameter\": \"<string>\",\n \"value\": \"<string>\",\n \"secondaryValue\": \"<string>\",\n \"values\": [\n \"<string>\"\n ]\n }\n ]\n }\n ],\n \"selectedColumns\": [\n \"<string>\"\n ]\n}"
response = http.request(request)
puts response.read_bodyconst url = 'https://prod-api.raisedonors.com/api/RecurringGift/query';
const options = {
method: 'POST',
headers: {Authorization: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
filter: '<string>',
skip: 123,
take: 123,
sortBy: 'createdate',
descending: true,
includeDetails: true,
groups: [
{
conditions: [
{
parameter: '<string>',
value: '<string>',
secondaryValue: '<string>',
values: ['<string>']
}
]
}
],
selectedColumns: ['<string>']
})
};
fetch(url, options)
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.error(err));{
"items": [
{
"id": 700089,
"organizationId": 50,
"createdDate": "2023-02-15T10:30:00Z",
"modifiedDate": "2025-11-15T03:00:00Z",
"profileNumber": "RG-700089",
"amount": 50,
"currencyCode": "USD",
"baseCurrencyCode": "USD",
"formattedAmount": "$50.00",
"donor": {
"id": 200456,
"name": "Priya Sharma",
"title": "Ms.",
"firstName": "Priya",
"middleName": null,
"lastName": "Sharma",
"suffix": null,
"email": "priya.sharma@example.org",
"phone": "(555) 012-3450",
"createdDate": "2023-02-15T10:30:00Z",
"modifiedDate": "2025-11-15T03:00:00Z",
"notes": "Monthly donor since February 2023. Volunteer organizer.",
"isOrganization": false,
"organizationName": null,
"crmKeyUrls": {
"VirtuousCRM": {
"crmKey": "100456",
"url": "https://app.virtuoussoftware.com/Generosity/Contact/Detail/100456"
}
},
"crmKey": "100456",
"crmSecondKey": null,
"isTestMode": false,
"isArchived": false,
"isGDPRDeleted": false,
"primaryAddress": {
"id": 400156,
"donorId": 200456,
"isPrimary": true,
"address1": "1542 Elmwood Lane",
"address2": null,
"city": "Springfield",
"state": "OR",
"postal": "97477",
"countryString": "United States",
"addressTypeDisplay": "Home"
},
"donorAddresses": [
{
"id": 400156,
"donorId": 200456,
"isPrimary": true,
"address1": "1542 Elmwood Lane",
"address2": null,
"city": "Springfield",
"state": "OR",
"postal": "97477",
"countryString": "United States",
"addressTypeDisplay": "Home"
}
],
"donorEmailAddresses": [
{
"id": 300156,
"donorId": 200456,
"typeDisplay": "Home Email",
"value": "priya.sharma@example.org",
"secondaryValue": null,
"isPrimary": true,
"isOptedIn": true,
"isValid": true,
"isEmail": true,
"isPhone": false,
"isBounced": false,
"countryCode": null
}
],
"donorPhoneNumbers": [
{
"id": 300157,
"donorId": 200456,
"typeDisplay": "Mobile Phone",
"value": "(555) 012-3450",
"secondaryValue": null,
"isPrimary": true,
"isOptedIn": true,
"isValid": true,
"isEmail": false,
"isPhone": true,
"isBounced": false,
"countryCode": "1"
}
]
},
"formattedFrequency": "Monthly",
"total": 1700,
"formattedTotal": "$1,700.00",
"successfulCycles": 34,
"startDate": "2023-02-15T00:00:00Z",
"startDateFormatted": "February 15, 2023",
"nextChargeDate": "2025-12-15T00:00:00Z",
"nextChargeDateFormatted": "December 15, 2025",
"statusText": "Active",
"gatewayId": 4180,
"gateway": "Riverside Hope — Stripe",
"paymentInfo": "Visa •••• 4242",
"expMonthAndYear": "04/2027",
"cardBrand": "Visa",
"paymentMethodCountryCode": "US",
"projectAllocation": [
{
"projectId": 3010,
"projectName": "General Operating Support",
"amount": 50,
"formattedAmount": "$50.00"
}
],
"isAnonymous": false,
"isTestMode": false,
"donorCoverCosts": false,
"motivationCode": "MGS-WEB",
"sourceCode": "monthly-giving-society",
"segmentId": null,
"segmentName": null,
"segmentCode": null,
"notes": "Monthly Giving Society member since February 2023.",
"comments": null,
"premium": null,
"crmKey": "700089",
"crmKeyUrls": {},
"merchantAccount": "riverside-hope-usd",
"hasPaymentFailed": false,
"isLegacy": false,
"currencyExchange": null,
"tribute": null,
"paymentMethodId": "pm_1QU4cxHs1Yb4Lm7B",
"costs": 1.75
},
{
"id": 700142,
"organizationId": 50,
"createdDate": "2024-04-01T08:12:44Z",
"modifiedDate": "2025-11-01T03:00:00Z",
"profileNumber": "RG-700142",
"amount": 25,
"currencyCode": "USD",
"baseCurrencyCode": "USD",
"formattedAmount": "$25.00",
"formattedFrequency": "Monthly",
"total": 500,
"formattedTotal": "$500.00",
"successfulCycles": 20,
"startDate": "2024-04-01T00:00:00Z",
"startDateFormatted": "April 1, 2024",
"nextChargeDate": "2025-12-01T00:00:00Z",
"nextChargeDateFormatted": "December 1, 2025",
"statusText": "Active",
"gateway": "Riverside Hope — Stripe",
"paymentInfo": "Mastercard •••• 8210",
"expMonthAndYear": "11/2025",
"cardBrand": "Mastercard",
"paymentMethodCountryCode": "US",
"projectAllocation": [
{
"projectId": 3012,
"projectName": "Year-End 2025 — Holiday Meals",
"amount": 25,
"formattedAmount": "$25.00"
}
],
"isAnonymous": false,
"isTestMode": false,
"donorCoverCosts": true,
"notes": null,
"hasPaymentFailed": true,
"isLegacy": false,
"costs": 0.98
}
],
"total": 214
}{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"groups[0].conditions[0].parameter": [
"'donorEmail' is not a valid parameter for this query type. Call Get Query Options for the parameters this query type supports."
]
},
"instance": "/api/RecurringGift/query"
}{
"type": "https://tools.ietf.org/html/rfc7235#section-3.1",
"title": "Unauthorized",
"status": 401,
"detail": "The Authorization header is missing, malformed, or carries a token that is expired or revoked.",
"instance": "/api/RecurringGift/query"
}RecurringGift query type to discover the parameters, operators, and result fields available, then send one or more groups of conditions here along with selectedColumns and your paging options.Authorizations
JWT Authorization header using the Bearer scheme. Example: "Authorization: Bearer {token}"
Body
The query request model.
- Option 1
- Option 2
Request body for advanced query endpoints such as donor and campaign queries.
A free-text search term applied to the results.
The number of records to skip before the first record returned. Use it with take to page through the results.
The maximum number of records to return in this page of results.
Field to sort by (case-insensitive). Valid options: createdate, createddatetime, email, firstname, gifts, giftsum, id, lastname, modifieddatetimeutc, name, phone
createdate, createddatetime, email, firstname, gifts, giftsum, id, lastname, modifieddatetimeutc, name, phone "createdate"
The direction to sort the results. Set to true to sort in descending order.
When true, includes the related records for each item in the response. Not every list has extra records to return, and it can slow down large result sets.
The logical type of query being executed.
0, 1, 2, 3, 4, 5, 6, 7, 8, 9 The groups of conditions that make up the query.
Show child attributes
Show child attributes
The list of result columns to return for each item.
Was this page helpful?