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.

  1. Read a financial page, up to 100 events, with since_cursor from your last page.
  2. In one local transaction, deduplicate each event_id, preserve its event, and save next_cursor. The same event can also arrive by signed webhook.
  3. Handle each event_type separately. For donor.updated, update the person as described above. For refund.created and refund.updated, upsert refunds by payload.id (see refunds). For donation.updated, upsert donations by payload.id. Replace a donation snapshot only when its revision compares 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.
  4. 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 with null.

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.

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_ready is false meanwhile. 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.