Add paycheck giving to your existing donation platform
Roster is an additional donation method alongside the card, ACH and other methods in your product. Your platform owns its reporting, dashboards and existing church management system (ChMS) integrations. Roster supplies structured donation and donor data; you decide how those records fit your existing model and connectors.
Connect a donation to your records
Subscribe your webhook endpoint to donation.updated and donor.updated. For donation.updated, the event's
church_id maps to the church ID returned by your import, entity_id and
payload.id identify one donation, and payload.enrollment_id identifies the
recurring enrollment. payload.donor_id and payload.donor.id identify the donor.
Preserve those mappings in your backend. Names and contact details are not unique
identifiers: never merge two people solely because their names match.
Every financial event, by webhook or from GET /v1/financial_events, carries
livemode: true in production, false in the sandbox. If the same receiver
gets events from both environments, use livemode to keep sandbox donations and
donors out of your production records.
Each enrollment belongs to the platform it was made through, and you receive
the donations of enrollments made through yours. When the church also works with
another partner (linked: existing_church), the enrollments made through that
partner are not included. Enrollments made on the church's own giving page are
not included for any platform, even the one that registered the church.
donor.updated reaches you for the donors whose donations you received, when
they change their contact details in a donor session you created.
The payload is a donation snapshot. method is paycheck; currency is USD.
All fields ending in _minor are integer cents: 5000 means USD 50.00.
amount_minor is the actual donation deposit before bank fees. It is never the
amount expected from an enrollment or the donor's income.
transfers describes recorded church and fee legs. Use each leg's role,
amount_minor, reference and status; do not calculate an assumed fee from a
marketing rate. residual_minor is the recorded remainder held for the donor.
Unknown historical amounts are null, not zero. An empty transfer list means no
recorded instruction is available in that snapshot.
Split among funds
When the enrollment was created with funds,
every snapshot carries funds: each fund's id, name, and percent or
amount as you sent them, plus amount_minor, its part of amount_minor in
cents. When the donation amount is known, the amounts always add up exactly to
it: each fund gets the donation in proportion to its percent or amount, rounded
down, and the cents
left by rounding go one by one to the funds with the largest remainders (a tie
goes to the fund listed first). A USD 10.00 donation split 66.67/33.33 reports
667 and 333. A split in dollars reports exactly those dollars when the donation
equals the gift: 30 and 20 on a USD 50 donation report 3000 and 2000. While the
donation's amount_minor is null, each fund's amount_minor is null too.
Without a split, funds is absent. All funds are paid to the same church bank account;
the split only labels the donation.
Interpret progress accurately
| Status | Meaning for your product |
|---|---|
received |
The donation deposit was recorded. No church transfer is implied. |
in_review |
The donation is in review, or a refund of it waits for Roster's review. Do not mark it paid to the church. |
pending_transfer |
Released or queued for transfer; no bank acceptance yet. |
transfer_submitted |
A transfer instruction/execution record exists; check the individual legs. |
transfer_accepted |
The settlement acceptance event is recorded. A later return is still possible. |
confirmed |
Reserved and not reported today. Even when reported, a later return remains possible. |
returned |
A return was reported. Inspect its leg, amount and reference; do not assume every leg was returned. |
failed |
Processing failed; do not invent a completed transfer. |
reversed |
The whole donation was refunded to the donor, in one refund or several (refunded_minor equals amount_minor). |
refunded_minor is what the donor's bank received from the donation's refunds,
counting only refunds that succeeded, in total and for each fund in funds. A
partial refund does not change status. Follow each refund through refund.created
and refund.updated; see refunds.
return_details contains the latest recorded return information. Keep the event
history as well as the current snapshot, and reconcile returned legs by their bank
reference. An enrollment cancellation does not erase donations already received.
The end of the flow on your page, an activated enrollment and an expected
amount do not prove that any money was received.
Approval before bank instruction
In production, a received deposit can remain pending while Roster completes its internal approval and the applicable checks. The sandbox has no approval step: an eligible deposit is instructed automatically at the next cutoff, and you do not need to ask Roster for anything. The checks still apply there: the processing window, held funds, enrollment and church status, and returned transfers. Bank acceptance and completion are separate steps; do not treat approval, a queued instruction, or the expected payday as a completed church transfer. Continue following the recorded transfer status and references.
For a donation that came through you, the fee leg carries Roster's fee and
your commission together, to Roster's account, and the church leg carries the
rest; Roster pays your commission to you separately. There is no separate
partner leg, and nothing is left over. In the sandbox this runs today, under a
test fee agreement that every sandbox workspace has from the day it was
created: Roster's standard 2% fee and a 0% commission for you, so the fee leg
carries Roster's fee only. Your actual fees come from your signed agreement and
apply in production. In production, until the configured bank integration
supports it, these transfers remain pending rather than omitting your
commission. A production donation also stays pending while no fee agreement
with you is in effect on the deposit date. No new
partner API call is required to approve these instructions. Existing donation status and reconciliation feeds
remain the source for your integration.
New transfer instructions also require confirmed ownership of the church's current bank account and a matching bank destination. Changing that account invalidates the previous ownership confirmation for payout purposes. An old account's confirmation cannot release transfers while the new account awaits verification.
Use donor data in your existing integration
When available, donor includes the stable donor ID, given and family names,
email, phone, postal address and profile update timestamp. These fields are for
your reporting and church integration within your authorized church portfolio.
Keep them in your protected backend, outside URLs, analytics, assistant prompts
and diagnostic logs. Pass the data your existing connector needs to the church's
ChMS under your established integration arrangements.
donor_data_status: unavailable with donor: null means a profile could not be
supplied, including older records without a stored profile. Do not invent a name,
match the donation to an unrelated person or silently discard the donation. Put
it in your integration's unresolved queue while preserving the financial record.
A supplied profile can still have missing fields: an unknown email, phone or
update time is null, and an unknown name or address line is an empty string.
Government identifiers, Social Security numbers, dates of birth, verification documents, bank credentials and API secrets are not included in these donor exports. Choose which permitted fields to consume in your connector; this feed does not provide a server-side field-selection setting.
The donor profile you receive is the one the donor gave through your platform:
in donation.updated, donor.updated, GET /v1/donor_sessions/{id} and the
partner portal. If the donor also gives through another platform and changes
their email there, or changes it directly with Roster, you keep the email they
gave you, and its updated_at does not change. For a donor whose details were
recorded before contract 1.3.0, you receive the latest profile until they next
give their details in a donor session you created.
When the donor changes their contact details in a donor session you created,
you receive donor.updated for each church where you already received that
donor's donations. Its entity_id is the donor ID, and its payload contains
your current donor, church_id and profile revision.
Upsert the person by donor ID, retaining the newest profile timestamp. A delayed
donation snapshot must not overwrite a more recent donor profile. Contact changes
are recovered through the same financial journal; they are not new donations.
Recover missed webhooks
Call GET /v1/financial_events using your ordinary partner API key. This journal
is separate from GET /v1/events, which recovers activity such as enrollment
changes. Keep a separate checkpoint for each feed.
- Read a financial page, up to 100 events, with
since_cursorfrom your last page. -
In one local transaction, deduplicate each
event_id, preserve its event, and savenext_cursor. The same event can also arrive by signed webhook. -
Handle each
event_typeseparately. Fordonor.updated, update the person as described above. Forrefund.createdandrefund.updated, upsert refunds bypayload.id(see refunds). Fordonation.updated, upsert donations bypayload.id. Replace a donation snapshot only when itsrevisioncompares greater, lexicographically, than the revision already stored for that donation. A delayed event must not overwrite newer state. Equal revision is a repeated state, not another donation. -
Continue until the API returns an empty page, then poll again later from the
cursor you already stored. An empty page never moves your position: it echoes
the cursor you sent, and on a first poll with nothing to read it returns
next_cursor: null. Never overwrite a stored cursor withnull.
sequence orders the committed integration journal, so it is suitable for replay
and auditing. It does not establish bank finality. revision orders snapshots
within a donation. occurred_at is the source event's business timestamp.
These values serve different purposes; do not substitute one for another.
A malformed or another partner's cursor returns 400. A suspended or revoked key
cannot read another organization's journal. Responses use Cache-Control: no-store.
The journal is established when financial integration is enabled. Its initial
bootstrap may include source events still retained in the stream (up to 24 hours);
that is not a complete historical import. Confirm the actual coverage start before
migrating an existing portfolio. A stack without the integration configured returns 503,
not a claim that there have been zero donations.
Today financial integration is enabled in the sandbox only. In production,
GET /v1/financial_events returns 503, and a completed donor session returns
donor: null.
Prove the integration in sandbox
Use synthetic churches and donor profiles. Complete a donor journey, simulate a realistic deposit through the documented sandbox operations, and observe the financial updates instead of treating the simulation's HTTP response as a payment result. Demonstrate a lost webhook recovered by polling, a duplicate ignored, an older snapshot delivered late, a returned church transfer, and two test donors kept apart: two test numbers, or one number with two dates of birth, are two donor IDs. Never merge donors by name. Check that the resulting records enter your own reporting model and your existing ChMS connector exactly once.
The same listFinancialEvents OpenAPI operation is discoverable through the
Scalar API MCP. Use your own API key. No shared Roster credential is
required, and no ChMS credential needs to be sent to Roster for this partner flow.
From a received deposit to an executed transfer
Sandbox deposits do not skip the processing window. The minimum retention is
120 minutes from the bank timestamp returned by the deposit simulator. Eligible
donations are then picked up by the weekday cutoff at 4:00 p.m. America/New_York,
which instructs the transfer on its own: no approval from Roster and no request
from you. A deposit that misses that cutoff waits for a later eligible run; two hours is
not a promise that the transfer will execute immediately. The deposit simulator's
202 returns both times for you: eligible_at, when the retention ends, and
next_run_at, the earliest cutoff that can pick the donation up.
A church with a weekly payout_schedule
receives on the next Monday instead (the next business day when Monday is a bank
holiday); until then the donation stays received.
Keep a separate active enrollment for this scenario. Its church must have
completed receiving-account setup and be eligible for payouts; a paused or
cancelled enrollment, an incomplete church setup, or held funds can prevent a
transfer. Read GET /v1/churches/{id}: receiving_account_verified confirms a verified
receiving destination, and payout_ready confirms current church-level eligibility.
Neither field promises that a particular donation can move. Check enrollment and
church status before treating an empty transfers
array as a failed webhook. Contact onboarding with the church and occurrence IDs
if the expected eligible run has passed without an update. Do not resubmit the
deposit to force progress.
Before using the late-return simulator, wait for an actual executed transfer in the financial snapshot. Use its transfer reference and amount; neither an accepted deposit request nor a received donation supplies that precondition. There is no partner operation that directly executes a settlement. Keep scheduled processing and returned-transfer acceptance as separate checks in your test plan.
Complete a church's legal setup
Donors can start paycheck giving only after Roster verifies the church: its
legal setup and its receiving account. Creating a church does neither. Turn
the giving button on for a church when payout_ready is true; until then,
the hosted flow tells the donor that paycheck giving isn't enabled for this
church yet and suggests another giving method. See
When a church is ready for donors.
For a church already in your portfolio, submit its
legal details with POST /v1/churches/{id}/verification. Use the church's actual authorized details
in production and a test EIN in sandbox. The API reference
lists the required fields. Roster processes the submission; the church does not
need to sign into Roster for this step.
A 202 accepted response confirms a queued request, not approval. Preserve the
request_id from the 202 body (it starts with chvr_) and your idempotency
key. If the submission failed before acceptance, retry unchanged data with the same key. Within the original request’s 30-day retry window,
an unchanged retry returns the same request_id even if the church has since
advanced to another state. This does not allow a new submission in that state.
Use a new key for corrected data. A 409 with the type
church-already-verified means the church is
already verified: treat it as done. Any other 409 means its legal details were
already received and only Roster can change them. A 503 means this environment's
verification service has not been configured; contact Roster instead of resubmitting.
Check GET /v1/churches/{id}: receiving_account_verified=true confirms only the
receiving account. payout_ready=true confirms that the current institutional
and account prerequisites are satisfied. Neither response proves that a transfer
has occurred. Continue using the financial journal for actual transfer outcomes.
If readiness remains false after both steps and the account came back verified
or already_verified, contact Roster with the church ID and the verification
request_id (chvr_…). For an account in_review, a Roster reviewer decides
and there is nothing to send. Do not submit fictitious approval or consent evidence.
Church status after verification
GET /v1/churches/{id} has no status field. church.onboarded, where it is
sent, announces only the first time a church becomes ready; no event
announces that Roster opened a review, stopped a church or held its payouts.
Read the church when you need its readiness; the portal's Churches page shows
the status label.
-
Bank account change. When a verified church changes its receiving account,
payouts wait until Roster confirms ownership of the new account, and
payout_readyisfalsemeanwhile. The church stays enabled: existing deductions continue and new donors can still enroll. - In review. A church in review keeps admitting donors unless a person at Roster paused new donors. Pausing new donors does not hold payouts.
- Suspended. Only a person at Roster stops a church. New donors then see that paycheck giving is unavailable for it; donors who already give can still change or cancel their gift. Payouts to a suspended church are held.
- Who is told. When Roster suspends a church, the church's team and the owners of the partner workspace that registered it receive an email saying the church is in review. Donors are not emailed. The email gives no reason, and neither does the portal.
So payout_ready=true means Roster verified the church and can pay its current
account. false on a church that was already verified usually means payouts are
held, not that donors are refused. Turn a church's giving button on the first time
its payout_ready is true. Leave the giving button on when payout_ready later
turns false: the hosted flow itself tells new donors when the church cannot take
new gifts. Show the church as in review, with no reason, in your admin: the words
Roster uses with the church.