Skip to content
Sign-ups

Take sign-ups on a link, not on a clipboard

Registration is a form, a waiver, a pile of documents and a roster that has to match all three. BallOBall builds the form, publishes it on a link a parent can complete without creating an account, stores the signed waiver with the version that was in force, and puts the confirmed registrant straight onto the roster. Those parts work now. The money half does not: card payments, payment plans and refunds are built, but they are inactive on this deployment until a payment provider is connected, so a paid form holds the place and tells the family the league will collect the fee directly.

Sign-up season runs on a PDF and a shoebox

The form goes out as a PDF, comes back as a photo of a photo, and somebody retypes it. The waiver is a printed page signed at the first practice, if it gets signed at all. Who paid lives in a spreadsheet, who is still missing a birth certificate lives in a group chat, and the roster only exists once an administrator has typed the same twenty names in for a third time. Then the age group fills up, and the eleventh family finds out by email three days late — by which point somebody has already promised them a place.

How it works

Four steps, no configuration

  1. Build the form

    Text, dropdown and multi-select questions, section headers, and file uploads with their own accepted types and size caps. Show-if logic keeps a question hidden until it applies. Set the fee, the capacity and the open and close dates. The form builder is part of the Registrations module, and the module has to be on for the league before the public form will accept anyone.

  2. Publish the link

    A parent opens the link and completes it without creating an account, for as long as the form is published and inside its window. They upload the documents you asked for, tick the waiver and type their name, and can enter a promo code and see the discount applied before they submit.

  3. Work the submissions

    Administrators read what came in, grant a scholarship or fee waiver against an individual registration, and record the outcome. With no payment provider connected, a paid form reserves the place and the league collects the fee directly — the sign-up still lands, the card just does not.

  4. Confirm, and the roster fills itself

    Confirming a registration creates that player on the roster. The step is designed to run once, so confirming the same registration again should not add a second copy of the player. Where a capacity is set, later sign-ups go to the waitlist and the next person is promoted when a place frees up.

Capabilities

What registration covers, and what waits on payments

Everything below is built and running today. Unbadged items are included on every plan; a badge means the capability needs a module added to your plan, a higher plan tier, or a piece of setup finished first.

9 of these are included on every plan. 2 groups need the Registrations / Camps modules. 4 are built and waiting on setup before they can be switched on.

  • Public sign-up page (no account required)

    A parent can open a shared registration link and sign a child up without creating an account.

    Only works while the form status is PUBLISHED, the league is not archived, the open/close window is current, and the REGISTRATIONS module is enabled — otherwise the page shows a closed/not-found message (registrationHandler.ts:1409-1427).

  • Online registration form builder

    Registrations module

    League administrators can build and publish registration forms with custom questions, an optional waiver, fees, capacity limits and an open/close window.

    Admin surface is role-gated to SUPER_ADMIN / PLATFORM_SUPPORT / LEAGUE_ADMIN (router.tsx:1006) and the league's REGISTRATIONS feature module must be enabled before the public form will accept anyone (registrationHandler.ts:1411).

  • Custom fields, conditional logic and document uploads on forms

    Registrations module

    Forms support text, select, multiselect, section and file-upload questions, with show-if conditional logic and per-field accepted file types and size caps.

    Same REGISTRATIONS module gate as the form itself; uploaded documents are retrieved through a separate authenticated presigned-URL route (routes/index.ts:1334).

  • Waivers with typed e-signature and versioned snapshot

    A form can require a waiver; the registrant ticks acceptance and types a signer name, and the exact waiver text and its version are stored with the registration and can be viewed later.

    Module-gated with the rest of registration. Signature capture is a typed name plus a checkbox, not a drawn signature or a third-party e-signature service.

  • Automatic roster enrollment from confirmed registrations

    Confirming a registration creates the corresponding player on the roster, and the operation is idempotent so an already-enrolled registrant is not duplicated.

    Fires on confirmation, which today is an admin action on the offline path; module-gated with the rest of registration.

  • Registration waitlist with automatic promotion

    Capacity-limited registrations and camps support a waitlist, and freeing a seat promotes the next person waiting.

    Only applies to forms and camps that have a capacity set; an uncapped form never triggers the capacity gate.

  • Promo / discount codes

    Admins can create, edit and delete promo codes on a registration form, and a registrant can enter a code and see the discount previewed before submitting.

    Admin CRUD is league-admin-only and module-gated; the preview endpoint is public but scoped to a single form.

  • Scholarships / fee waivers

    A league admin can grant a scholarship or fee waiver against an individual registration, and the waived amount is recorded on the registration.

    Admin-only action (LEAGUE_ADMIN of the form's league); it records a waived amount rather than moving money.

  • Camps and clinics with sessions, capacity and online sign-up

    Camps module

    Leagues can publish camps and clinics with session schedules and capacity limits, browse them on a public page, and check registrants in; free camps confirm a place immediately.

    Requires the MODULE_CAMPS feature module to be enabled (campsHandler.ts:326). Free camps (price 0) complete end to end today; PAID camps do NOT — with Stripe unconfigured the checkout is skipped, the reserved seat is released and no enrolment is created (campsService.ts:774-788).

  • Parent portal — 'My Registrations' across leagues

    A signed-in guardian can see their own registrations across every league in one place.

    Requires an authenticated account; anonymous registrants who never signed up for a login do not get this view.

  • Itemized registration receipts

    An itemized receipt for a registration can be viewed and printed by the registration's own guardian or by a league admin of that form's league.

    Requires sign-in (RequireAuth) and passes an explicit viewer-access check; there is no receipt to render for an amount that was never charged.

  • Versioned platform refund policy pinned at purchase

    Refund terms are stored as versioned policy records, and the version in force at the time of purchase is the one a later refund is judged against.

    Authoring is restricted to the platform owner (SUPER_ADMIN) — a league cannot write its own refund policy through this surface. Read access adds PLATFORM_SUPPORT.

  • Card payment for registration fees (Stripe Checkout)

    Needs a connected payment provider

    The code path that creates a Stripe Checkout Session for a registration fee is implemented, and until Stripe keys are configured a paid form instead reserves the registrant's spot and tells them the league will collect the fee directly.

    DORMANT. stripeService detects the PASTE_* placeholder secret and returns a null client (stripeService.ts:87-95), so createRegistrationCheckout returns {configured:false} and the submission falls back to the offline message 'Online payment isn't enabled yet' (registrationHandler.ts:1878-1879). The registration row is still created; the operator collects the fee offline and confirms the registrant from the ledger.

  • Payment plans / installments with automatic recurring charges

    Needs a connected payment provider

    Forms can offer a monthly payment plan with an optional deposit; the card is vaulted at checkout and a daily scheduled job charges each installment as it falls due, with retry backoff and a delinquency alert after repeated failures.

    DORMANT for the same reason as checkout — chargeInstallmentOnline returns {charged:false, reason:'stripe_not_configured'} when Stripe keys are placeholders (registrationPaymentService.ts:1136), and the CDK comment confirms the daily rule is 'a harmless zero-cost tick on every stamp that hasn't configured Stripe' (api-stack.ts:2224-2225). The schedule rows are still written, and an admin marks each installment PAID or WAIVED from the ledger (POST /registration-installments/{installmentId}/status).

  • Sibling / multi-child discounts in one submission

    Needs a connected payment provider

    A parent can register several children in a single submission and a configurable sibling discount is applied to each additional child, with the price summary shown before submitting.

    Requires the REGISTRATIONS module; the discount percentage is set per form by an admin. The discount arithmetic runs whether or not online payment is live, but the resulting amount can only be collected offline while Stripe is dormant.

  • Registration refunds, including partial refunds

    Needs a connected payment provider

    An admin can refund a paid registration in full or in part, with the refundable balance tracked cumulatively and an idempotency key that prevents a double-click refunding twice.

    DORMANT — the refund path throws 'Stripe is not configured — cannot process refund.' when keys are placeholders (registrationPaymentService.ts:789). Admin-only. Refunds also reconcile refunds issued directly from the Stripe dashboard once keys are live.

Questions

Before you ask

Can we take card payments through the form today?

Not on this deployment. The Stripe Checkout path, the PayPal redirect, monthly payment plans, refunds and donation pages are all written and shipping, but they stay inert until a payment provider is connected. With no keys configured, a paid form reserves the registrant's place and tells them the league will collect the fee directly. Paid camps are the strictest case — the held seat is released rather than kept, so run free camps end to end and take paid ones offline for now. Everything else on this page works without a payment provider.

Does a parent need an account to sign up?

Not to sign up. The public form takes a submission from someone who has never logged in, provided the form is published, the league is active and you are inside the open and close window. Two things do need a login: the 'My Registrations' view that shows a guardian everything they have registered across leagues, and the itemised receipt.

Is the waiver a legally binding e-signature?

It is a tick box plus a typed signer name, stored with the exact waiver text and the version in force at the time, so you can open a registration months later and see precisely what that person agreed to. It is not a drawn signature, and it is not a third-party e-signature service with its own audit certificate — BallOBall does not provide one. Weigh that against your own legal advice before you rely on it.

What happens when an age group fills up?

Where a capacity is set on the form or the camp, further sign-ups go to a waitlist, and freeing a place promotes the next person waiting. Leave the capacity field blank and there is no gate to fire, so there is no waitlist either. That is a decision you make on the form rather than a default applied for you.

Do we have to retype registrants into the roster?

No. Confirming a registration creates the corresponding player on that roster, and the step is designed to be idempotent: confirming someone who is already enrolled is meant to leave the roster as it is rather than add a second copy. Confirmation is an administrator action, which suits the offline-payment path: you confirm once the fee has actually arrived.

Is registration included, or is it an extra?

The form builder, the custom fields and the document uploads sit in the Registrations module, and camps and clinics sit in the Camps module. Both are added to your plan, and the relevant module has to be enabled on the league before the public page will take a submission. The parts around them — the waiver and its versioned snapshot, promo codes, scholarships, the waitlist, roster enrolment, receipts and the parent portal — are not separate line items on top of that.

Open your next intake on a link

Public leagues need no account to open.