Registration Platform
A registration platform integration captures donations made during club registrations and forwards them to ASF. Every request from this channel carries registration-specific identifiers in sourceMetadata.
Flow at a glance
Section titled “Flow at a glance”- The registration platform calls
GET /accountsto validate that the club has activated donations on that platform and obtain the details of the fundraising cause - The fundraising cause is displayed to the club member during the registration flow
- The club member completes a registration with an attached donation.
- The integration job polls (or receives a webhook for) settled registration donations.
- For each donation, validate the fundraiser then
POST /donations. - When the bank settles,
POST /remittanceskeyed bybankReferenceId.
Source metadata fields
Section titled “Source metadata fields”A Registration Platform channel populates these:
| Field | Example | Required |
|---|---|---|
registrationId | Registration Platform registration id | No |
externalId | Registration Platform Unique Member ID | No |
Add any further registration-side identifiers (seasonId, productId, memberId) — sourceMetadata accepts arbitrary properties and ASF stores them verbatim.
Validating Club Activation
Section titled “Validating Club Activation”The GET /accounts endpoint both validates the activation and returns the details of the fundraising cause for display in your UI.
/accounts/ASF123456?channel=XYZ%20Registration%20Platform{ "apiVersion": "1.0.21", "correlationId": "3ea4aba4-d474-4208-b814-fa35e1ef2eb3", "method": "/accounts/ASF123456", "data": { "items": { "name": "Wombat Park Softball Club", "memberNumber": "ASF123456", "abn": "9876543210", "products": [ { "name": "XYZ Registration Platform", "description": "Your support will help our club upgrade our digital scoreboard.", "status": "Activated", "logo": "https://assets.asf.org.au/images/logo.png", "coverImage": "https://assets.asf.org.au/images/cover_image.png", "donationAmounts": [10, 20, 50, 100], "story": "" } ] } }}Posting a donation
Section titled “Posting a donation”{ "source": { "channel": "XYZ Registration Platform", "fundraiserId": "ASF123456" }, "donor": { "email": "jenny@example.com", "firstName": "Jenny", "lastName": "Smith", "billingAddress": { "postalCode": "2604", "country": "Australia" }, "verifiedEmail": "jenny@example.com" }, "transaction": { "id": "504789900", "date": "2026-05-11T11:00:00+10:00", "amount": "50" }, "sourceMetadata": { "registrationId": "954uOVtyJz7K" }}Implementation
Section titled “Implementation”Payment splitting and settlement at point of transaction is preferred to ensure that donors receive their donation receipt immediately. If payment splitting is not feasible, partners have scheduled a job that pages settled registrations and forwards donations. The reference implementation below is a Node.js worker; adapt the trigger to whatever job runner you use (Cloudflare Cron Trigger, Vercel Cron, AWS EventBridge, a long-running container, etc.).
import { randomUUID } from 'node:crypto';import { fetchSettledRegistrations } from './registration-platform.js';import { db } from './db.js';
const BASE = process.env.ASF_BASE_URL;
function asfHeaders(correlationId) { return { client_id: process.env.ASF_CLIENT_ID, client_secret: process.env.ASF_CLIENT_SECRET, 'X-Correlation-ID': correlationId, 'Content-Type': 'application/json', };}
export async function forwardSettledDonations() { const since = await db.lastSettledAt(); const registrations = await fetchSettledRegistrations({ since });
for (const reg of registrations) { if (!reg.donation || reg.donation.amount <= 0) continue; if (await db.isAlreadyForwarded(reg.id)) continue;
const correlationId = randomUUID(); const fundraiserId = reg.donation.fundraiserId;
// 1. Validate const v = await fetch( `${BASE}/fundraisers?channel=XYZ%20Registration%20Platform&id=${fundraiserId}`, { headers: asfHeaders(correlationId) }, ); if (!v.ok) { await db.recordFailure(reg.id, correlationId, await v.text()); continue; }
// 2. Post the donation const body = { source: { channel: 'XYZ Registration Platform', fundraiserId, }, donor: { email: reg.member.email, firstName: reg.member.firstName, lastName: reg.member.lastName, billingAddress: reg.member.address, verifiedEmail: reg.member.emailVerified ? reg.member.email : undefined, }, transaction: { id: reg.donation.transactionId, date: reg.donation.settledAt, amount: String(reg.donation.amount), }, sourceMetadata: { registrationId: reg.id, externalId: reg.donation.externalId, seasonId: reg.season.id, productId: reg.productId, }, };
const d = await fetch(`${BASE}/donations`, { method: 'POST', headers: asfHeaders(correlationId), body: JSON.stringify(body), }); if (!d.ok) { await db.recordFailure(reg.id, correlationId, await d.text()); continue; }
const { data } = await d.json(); await db.recordSuccess(reg.id, data.items.donationReferenceId, correlationId); }}Reconciling remittances
Section titled “Reconciling remittances”Your payment processor may settle to ASF’s bank in batches. Group all donation references covered by a single bank transaction into one remittance entry:
{ "source": { "channel": "XYZ Registration Platform", "fundraiserId": "ASF123456" }, "remittances": [ { "bankReferenceId": "NAB-20260511-0001", "amount": 1500.00, "donationIds": [ "0000468-000788", "0000468-000791", "0000468-000795" ] }, { "bankReferenceId": "NAB-20260511-0002", "amount": 500.00, "donationIds": ["0000468-000798"] } ]}Things that have bitten partners before
Section titled “Things that have bitten partners before”- Donor email is often the registrant. When a parent registers a child, the donation receipt should go to the parent. Most partners send the registrant’s email rather than the child’s. Verify this matches your fundraiser-side expectations.
- Donations are non-refundable. As per ASF policy. If registration fees are refunded, the refund amount should exclude any donation amounts.