Grain’s giving API is a connected resource graph:
Lists include compact, one-level summaries. Fetch large related collections through their own paginated endpoints with donorId, householdRef, givingBatchId, or settlementId filters instead of expecting nested unbounded data.
Map your giving funds
Call GET /api/v1/funds to populate a fund dropdown. Each result contains the Grain fund id and display name; store the selected id in your integration and send it as fundId when creating contributions.
The list includes all active, assignable funds in the authenticated church, ordered by name, with no pagination. Inactive funds and summary funds that contain subfunds are excluded. A church with no assignable funds receives { "funds": [] }.
Enable funds:read on your organization API key or Grain-managed OAuth app. For OAuth, add this scope to the app’s registered scopes and have users consent again; the signed-in user must have View funds permission. Send the OAuth access token as the bearer credential and include X-Organization-Id on the request. Existing keys and grants do not gain this scope automatically; create a replacement API key with fund access if needed.
Retry contribution creates safely
Send an Idempotency-Key header with POST /api/v1/contributions. Choose a stable key for each gift row, such as og-gift-38217-general, and reuse it if a request times out or the connection drops.
- The first successful create and subsequent retries return
201 with the same contribution ID. Concurrent retries create one contribution.
- Reusing a key with different validated data returns
409 Conflict. Use PATCH to edit the original gift, or a new key for a separate gift.
- If the original contribution was deleted, its key returns
409; a retry cannot recreate it.
- Keys accept 1–200 non-space ASCII characters and remain reserved for the lifetime of the organization. Keep keys free of personal information.
- Keys are scoped to the organization and credential: the OAuth application, organization API key, or unmanaged OAuth user. OAuth token refresh preserves the namespace; replacing an API key starts a separate namespace.
- Authentication, permissions, and input validation apply to retries. A replay returns the contribution’s current representation, including subsequent edits, with current identity-masking permissions.
- Validation failures do not reserve the key. Correct the input and retry.
The header is optional for compatibility. An externalId alone does not prevent duplicates: split gifts can legitimately share it. Use a different idempotency key for each split row, and reuse that row’s key for retries. Before replaying older imports that did not use a key, reconcile your saved Grain contribution IDs; adding a key does not deduplicate an earlier unkeyed request.
Who owns the totals?
- Contribution amounts, processing fees, and stored net amounts are the source for a giving batch. Grain recalculates batch gross, absolute fees, net, fund splits, and distinct gift count whenever membership or money changes.
- Settlement
recordedTotals are provider or client-owned values and remain independently editable.
- Settlement
linkedContributionTotals are calculated from current linked contributions. Compare the two objects to identify import or membership differences.
Dates such as contribution date, batch date, and estimated arrival date are calendar days. Grain returns existing v1 date fields as UTC ISO strings, with calendar days represented at UTC midnight. Audit timestamps such as createdAt, updatedAt, and committedAt are real instants.
Create a manual batch
If givingIntegrationId and externalBatchId are omitted, Grain uses its non-syncing manual giving integration and generates an external ID. Empty open batches are valid.
contributionIds replaces batch membership atomically. A contribution already owned by another batch produces 409 Conflict; move it explicitly with PATCH /api/v1/contributions/{id} before the bulk membership write.
Link a whole batch to a settlement
Settlement writes accept both individual contributionIds and whole givingBatchIds. Attaching a batch also attaches all of its contributions in the same transaction.
All donors, funds, merchants, integrations, contributions, batches, and settlements referenced by a write must belong to the authenticated organization. Partial-batch or conflicting settlement ownership returns 409 without applying a partial change.
Accounting freeze behavior
CRUD never posts a journal entry, links a bank transaction, or reconciles a settlement. MATCHED, RECONCILED, and batch SETTLED are accounting-owned states and cannot be created through ordinary API edits.
Unfrozen contributions, batches, and settlements can be edited, moved, or deleted. Once a resource is posted, cleared, bank-linked, or otherwise accounting-frozen, conflicting writes return 409 Conflict. Deleting an unfrozen batch or settlement unlinks and preserves its contributions; deleting a contribution recalculates its former batch.
Household members
Use the member routes to keep current membership, position, and primary contact consistent:
POST /api/v1/households/{id}/members
PATCH /api/v1/households/{id}/members/{donorId}
DELETE /api/v1/households/{id}/members/{donorId}
Moving a donor refreshes both affected household aggregates. Send null in donor or household PATCH requests to clear documented nullable fields.
Give integrations separate contributions, batches, and settlements
scopes. Write scopes imply read, but they do not grant journal-posting
permission.
Last modified on September 15, 2026