Docs/SideBet/Payment rails

Payment rails: Aeropay & Aerosync

Our rail by default, yours if you have one. How the Aeropay pay-by-bank rail and its Aerosync bank-linking widget fit into round-ups.

The rail model

Round-ups accrue in SideBet regardless of rail. The rail only decides two things: how a user’s bank gets linked, and which processor pulls the batched transfer when their threshold is reached. Every merchant runs in one of two modes.

Platform
Stripe

The default. ACH Direct Debit with Financial Connections for instant bank linking. Nothing to configure; SideBet holds the processor relationship.

Bring your own
Aeropay · CoinFlow

You already have a processor contract, or you operate in a category Stripe restricts. Your credentials are stored encrypted on your merchant record and every call runs under your account.

Why sportsbooks pick Aeropay

Stripe treats sportsbooks and iGaming as a restricted category for ACH. Aeropay is licensed for iGaming, offers guaranteed funds on pending transfers, and its users can reuse a bank linked at any other Aeropay merchant. The trade-off is one or two extra screens the first time a user links, described below.

Aeropay

Aeropay is a pay-by-bank network. A user is identified by mobile number, holds an Aeropay account that spans every merchant on the network, and links one or more bank accounts to it. Merchants debit those accounts through Aeropay’s API. SideBet uses it for the single batched round-up transfer, never per purchase.

Set up the rail

1

Get Aeropay credentials

From your Aeropay dashboard: numeric merchant ID, API key and API secret. Sandbox and production are separate.

2

Configure the merchant in the SideBet admin

Merchants → Payment Providers → Aeropay. Enter the three values and pick the environment. Credentials are encrypted at rest.

3

Register the webhook

Admin → Merchants → Webhook URL gives you a signed URL. Add it in Aeropay for transaction_completed, transaction_declined and transaction_voided.

4

Go live

Flip the environment to production. The SDK loads Aeropay's real Aerosync widget automatically. Nothing changes in your integration.

What changes for the user

Aeropay needs a legal name and mobile number to create the user, and it sends a one-time code that must be confirmed before any bank can be linked. The Link SDK handles both. Pass name and phone in the link token and the details screen is skipped, but the code step always applies to a new Aeropay user.

The code is not always a text message

Aeropay chooses the channel and tells you which it used. On our sandbox merchant it emails the code, so the SDK says “Enter the code we emailed you” and names the inbox. Do not hard-code SMS wording.

ScreenWhenLink session kind
A little about youName or phone missing from the link tokencollect_details
Enter the code we sent youAlways, for a new Aeropay user. The session carries mfaChannel, email or smsconfirm_mfa
Select your bankAlways, onceaerosync_widget

Aerosync

Aerosync is Aeropay’s bank-linking widget, the equivalent of Plaid Link or Stripe Financial Connections. The user searches for their institution, signs in with their bank credentials inside the widget, and picks an account. Credentials go to the bank; neither SideBet nor your servers see them. Aerosync returns a connectionId, which SideBet exchanges with Aeropay for a verified bank account.

In production the SDK loads the widget from Aeropay’s CDN and mounts it in your page. In sandbox mode SideBet serves a built-in replica so you can test the whole flow without Aeropay credentials.

Two sandboxes

Aeropay’s own sandbox is the real thing: real tokens, real Aerosync at sandbox.aerosync.com. It needs credentials from Aeropay. Alongside it we run a built-in replica so you can build the whole flow before those arrive. Which one a merchant uses is decided by its credentials, so both run against the same deployment.

ParameterTypeDescription
000000
Aeropay sandbox codeThe one-time code Aeropay accepts in its sandbox. Other values return AP111.
Any institution
replica bank pickerThe replica lists twelve banks. All accept the same test login; the one you pick is the name that appears on the linked account.
sync / no_mfa
replica username / passwordLinks straight through to the account list.
sync / mfa
replica username / passwordAdds a security question. The answer is "correct".
123456
replica codeThe code the replica accepts. A number ending in 0000 makes it use the SMS channel instead of email.
wrong
replica passwordShows the "Invalid credentials" state.

The link flow, end to end

The Link SDK drives this for you. The endpoints are documented so you can build your own UI or debug a session; all of them take the link token as a bearer credential and run on your origin.

POST/api/link/bank-link

Start a bank link. Returns the next thing the user has to do.

Response — one of three session kindsjson
// Aeropay needs name and phone first
{ "session": { "kind": "collect_details", "requiredFields": ["lastName", "phone"] },
  "rail": { "railMode": "BYO", "pspType": "AEROPAY" } }

// That phone already has an Aeropay account
{ "session": { "kind": "confirm_mfa" }, "rail": { "railMode": "BYO", "pspType": "AEROPAY" } }

// Ready to link: open the Aerosync widget with this token
{ "session": { "kind": "aerosync_widget", "widgetUrl": "…", "token": "wtok_…", "environment": "sandbox" },
  "rail": { "railMode": "BYO", "pspType": "AEROPAY" } }
PUT/api/link/consumer

Supply the details Aeropay asked for, then call bank-link again.

Bodyjson
{ "firstName": "Ada", "lastName": "Lovelace", "phone": "+14155550123" }
POST/api/link/confirm-mfa

Submit the SMS code. On success, call bank-link again.

Body and errorjson
{ "code": "123456" }

// wrong code → 400
{ "error": "Aeropay POST /v2/confirmUser failed: Invalid verification code (AP111)" }
// The SDK rewrites this as "That code didn't match. Check the last 6 digits we texted you and try again."
POST/api/link/bank-link/complete

Exchange the Aerosync connectionId for a verified bank and record the debit authorization.

Body and responsejson
{ "payload": { "connectionId": "conn_…" },
  "mandate": { "userAgent": "…", "acceptedAt": "2026-09-02T20:11:28Z" } }

// → 200
{ "profile": { "pspType": "AEROPAY", "status": "VERIFIED", "bankName": "Chase", "bankLast4": "4321",
               "mandateAcceptedAt": "2026-09-02T20:11:28Z" },
  "rail": { "railMode": "BYO", "pspType": "AEROPAY" } }

Transfers and webhooks

When a user’s accrued round-ups reach their threshold, SideBet creates one Aeropay transaction for the total. Aeropay reports the outcome by webhook; SideBet verifies the signed URL token, updates the ledger, and forwards a roundup.settled or roundup.returned event to your webhook URL.

Aeropay topicDebit statusYou receive
transaction_completedSETTLEDroundup.settled — wallet credited, accrual reset
transaction_declinedFAILEDroundup.returned — accrual restored, retried at the next threshold
transaction_voidedRETURNEDroundup.returned

Sandbox controls

In sandbox the replica settles every transaction after five seconds. Force a different outcome to test your webhook handling:

Force an outcomebash
curl -X POST https://api-production-610a.up.railway.app/mock/aeropay/control/transactions/$TXN_ID/declined
# outcomes: declined · voided · processed

Hedge, Inc. · Payments for the people who use them