curl --request POST \
--url https://api.grainledger.com/api/v1/households/{id}/members \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"donorId": "<string>"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({donorId: '<string>'})
};
fetch('https://api.grainledger.com/api/v1/households/{id}/members', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.grainledger.com/api/v1/households/{id}/members"
payload = { "donorId": "<string>" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"id": "<string>",
"name": "<string>",
"primaryContactId": "<string>",
"primaryContact": {
"id": "<string>",
"firstName": "<string>",
"lastName": "<string>",
"organizationName": "<string>",
"donorType": "INDIVIDUAL",
"email": "<string>",
"phone": "<string>",
"householdRef": "<string>",
"householdName": "<string>",
"householdPosition": "PRIMARY",
"isPrimaryContact": true,
"membershipStatus": "VISITOR",
"isActive": true,
"totalGiving": 123,
"givingTrend": 123,
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
},
"externalIds": [
{
"platform": "<string>",
"externalId": "<string>"
}
],
"members": [
{
"id": "<string>",
"firstName": "<string>",
"lastName": "<string>",
"organizationName": "<string>",
"donorType": "INDIVIDUAL",
"email": "<string>",
"phone": "<string>",
"householdRef": "<string>",
"householdName": "<string>",
"householdPosition": "PRIMARY",
"isPrimaryContact": true,
"membershipStatus": "VISITOR",
"isActive": true,
"totalGiving": 123,
"givingTrend": 123,
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
}
],
"aggregates": {
"memberCount": 123,
"lifetimeGiving": 123,
"lastGiftDate": "2023-11-07T05:31:56Z"
},
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}Add household member
Moves the donor from an existing household when necessary and atomically refreshes both households. Cross-organization references are rejected.
curl --request POST \
--url https://api.grainledger.com/api/v1/households/{id}/members \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"donorId": "<string>"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({donorId: '<string>'})
};
fetch('https://api.grainledger.com/api/v1/households/{id}/members', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.grainledger.com/api/v1/households/{id}/members"
payload = { "donorId": "<string>" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"id": "<string>",
"name": "<string>",
"primaryContactId": "<string>",
"primaryContact": {
"id": "<string>",
"firstName": "<string>",
"lastName": "<string>",
"organizationName": "<string>",
"donorType": "INDIVIDUAL",
"email": "<string>",
"phone": "<string>",
"householdRef": "<string>",
"householdName": "<string>",
"householdPosition": "PRIMARY",
"isPrimaryContact": true,
"membershipStatus": "VISITOR",
"isActive": true,
"totalGiving": 123,
"givingTrend": 123,
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
},
"externalIds": [
{
"platform": "<string>",
"externalId": "<string>"
}
],
"members": [
{
"id": "<string>",
"firstName": "<string>",
"lastName": "<string>",
"organizationName": "<string>",
"donorType": "INDIVIDUAL",
"email": "<string>",
"phone": "<string>",
"householdRef": "<string>",
"householdName": "<string>",
"householdPosition": "PRIMARY",
"isPrimaryContact": true,
"membershipStatus": "VISITOR",
"isActive": true,
"totalGiving": 123,
"givingTrend": 123,
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
}
],
"aggregates": {
"memberCount": 123,
"lifetimeGiving": 123,
"lastGiftDate": "2023-11-07T05:31:56Z"
},
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}{
"code": "forbidden",
"message": "You do not have access to this organization.",
"error": "You do not have access to this organization."
}Authorizations
Organization API key created in Grain Settings → API. The key determines the organization and is limited to its assigned resource scopes. Write scopes imply the corresponding read scope.
Headers
Required with OAuth access tokens for resource requests. Omit only for GET /api/v1/organizations. Optional with organization API keys; if supplied with an API key, it must match the key's organization.
Path Parameters
Resource ID
Body
Response
Updated household
Household detail with a compact member collection, normalized external IDs, primary contact, and live giving aggregates.
Compact donor identity, household, membership, and giving summary used in list and relationship responses.
Hide child attributes
Hide child attributes
INDIVIDUAL, HOUSEHOLD, ORGANIZATION, DAF_CUSTODIAN, FOUNDATION, ESTATE PRIMARY, SPOUSE, CHILD, OTHER VISITOR, REGULAR_ATTENDER, MEMBER, INACTIVE, FORMER Hide child attributes
Hide child attributes
INDIVIDUAL, HOUSEHOLD, ORGANIZATION, DAF_CUSTODIAN, FOUNDATION, ESTATE PRIMARY, SPOUSE, CHILD, OTHER VISITOR, REGULAR_ATTENDER, MEMBER, INACTIVE, FORMER