Migration guides
Migrating from Flywire
Flywire and Qualy solve the same problem: a student abroad paying an education provider in their own currency. Flywire's API is only open to selected partners, so many institutions work with it through its portal and reconciliation files. Qualy gives you the same flows through a public API, plus payouts to agents and partners on the same payment.
Concept map
| Flywire | Qualy | Notes |
|---|---|---|
| Student / payer record | Contact | The student. Whoever actually pays (a parent, a sponsor) is the payer on the payment. |
| Payment request / invoice | Payment intent | With line items, a dueAt and tax. |
| Payer portal / checkout link | Payment portal via links.short, or a reusable link | Branded with your subdomain or custom domain. |
| Paying in home currency | settlementCurrency | The student pays in BRL, COP, INR… you receive your currency. |
| Payment plans | Orders with installment payments, or financing | Financing covers buy-now-pay-later and card installments. |
| Payer identity checks | Built into the portal | Qualy asks for what a method needs (e.g. CPF for PIX). Over the API you get a 424 listing what's missing. |
| Reconciliation file | Webhooks + queries | Each payment is reconciled as money arrives. No file to import. |
| Agent commission handled outside Flywire | Payment splits | Pay agents and partners from the same payment. |
| Refund request | Refund | — |
Payment requests → payment intents
Where you'd create a payment request (or upload a batch to Flywire), create a payment intent per student. Use your student or invoice number as number so your finance team sees the same reference:
const res = await fetch('https://api.qualyhq.com/v1/payment-intents/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'ApiKey your-api-key-here',
'X-TENANT-ID': 'your-tenant-id-here',
},
body: JSON.stringify({
intentType: 'ad-hoc',
number: 'STU-88231-T1',
contact: 'maria.gomez@example.com',
currency: 'AUD',
dueAt: '2027-01-15T00:00:00.000Z',
items: [
{ name: 'Tuition fee — Term 1', category: 'Course', amount: 820000 },
{ name: 'OSHC', category: 'Insurance', amount: 61000 },
],
}),
});
const { data: payment } = await res.json();
console.log(payment.links.short); // what you'd send instead of the Flywire link
The student opens the link, picks a currency and a method available in their country, and pays. If they pay part now and the rest later, the payment moves to paid-partial and then paid-full.
Payment plans
A term fee split into instalments is an order with one installment payment per due date. Qualy chases each one when it falls overdue (see Dunning) and lets the student clear the remaining instalments early. If you'd rather a lender carries the plan, offer financing on the portal.
Agents and partners
With Flywire you typically receive the full amount and pay the agent's commission separately. In Qualy you attach the split when you create the payment, and the commission is paid out when the student's money settles:
body: JSON.stringify({
type: 'keep-percentage',
currency: 'AUD',
amount: 697000, // what the school receives
keep: 123000, // what the agent (you) keeps
partnership: 'usyd',
contact: 'maria.gomez@example.com',
paymentIntent: '661e9bbfe0eb901d6b2f2670',
item: '661e9bc0e0eb901d6b2f2682',
}),
See Creating payment splits for every split type and how to preview payouts before you commit.
Reconciliation
Instead of downloading a reconciliation file, listen for:
paymentIntents.update— the payment'sstatuschanged (paid-partial,paid-full,overdue…).transactions.create— money arrived (or was refunded), with amount, currency and method.splitIntents.update— a partner's payout moved.
See Setting up webhooks. For a one-off backfill, page through GET /v1/payment-intents with query filters.
Moving open payments
Let payments already issued through Flywire finish there. Start new payment requests in Qualy from a cut-off date, so no student receives two links for the same fee.