Appearance
Create a Payment Experience
This flow walks you through generating a shareable payment link for your client. You start at the root, browse payment recipients, pick a destination, and receive a URL that the payer can use to complete their payment.
Step 1 — Get the Root
bash
curl 'https://api-platform.demo.flywire.com/agents/v2/' \
--header 'X-Authentication-Key: YOUR_API_TOKEN'python
import requests
TOKEN = "YOUR_API_TOKEN"
HEADERS = {"X-Authentication-Key": TOKEN}
root = requests.get("https://api-platform.demo.flywire.com/agents/v2/", headers=HEADERS).json()javascript
const TOKEN = "YOUR_API_TOKEN";
const HEADERS = { "X-Authentication-Key": TOKEN };
const root = await fetch("https://api-platform.demo.flywire.com/agents/v2/", { headers: HEADERS }).then(r => r.json());Step 2 — Follow payment-recipients
Find the link whose rel ends with payment-recipients and GET its href.
bash
curl "$PAYMENT_RECIPIENTS_HREF" \
--header 'X-Authentication-Key: YOUR_API_TOKEN'python
recipients_link = next(
l for l in root["links"]
if l["rel"].endswith("payment-recipients")
)
recipients = requests.get(recipients_link["href"], headers=HEADERS).json()javascript
const recipientsLink = root.links.find(
l => l.rel.endsWith("payment-recipients")
);
const recipients = await fetch(recipientsLink.href, { headers: HEADERS })
.then(r => r.json());The response contains an items array. Each item has a name, description, and links.
Step 3 — Navigate the Recipient Tree
Items come in two flavours:
| Type | Has country? | Key link |
|---|---|---|
| Group | No | payment-recipients — drill down into sub-items |
| Destination | Yes | set-up-payment-experience or standard-payment-experience |
Decision tree
- Look at an item's properties.
- No
countryfield → it's a group. Follow itspayment-recipientslink to get child items. - Has
countryfield → it's a destination. Proceed to Step 4.
For groups, follow the payment-recipients link the same way you did in Step 2, and repeat until you reach a destination. Note the index of the destination within its parent collection — you'll need it in Step 4 to find the right action link.
Items don't carry their own links array
Action links for an item (such as standard-payment-experience) are emitted on the parent collection's links[], each carrying an anchor field like "#/items/0". To find the action for item N, search the collection's links[] for a link whose rel matches and whose anchor equals #/items/N. See Anchoring.
python
# Find the first destination in the current `recipients` collection.
# In real code, you'd let the user pick — here we take the first.
destination_idx, destination = next(
(i, it) for i, it in enumerate(recipients["items"]) if "country" in it
)javascript
const destinationIdx = recipients.items.findIndex(it => "country" in it);
const destination = recipients.items[destinationIdx];Step 4 — Create the Payment Experience
Once you have a destination, check which link is available:
Option A — standard-payment-experience
No setup required. POST directly (empty body) and you'll receive a 307 Temporary Redirect to the prepared experience. Unlike a 303, a 307 preserves the POST method when followed — most HTTP clients handle this automatically with redirect: "follow" (fetch) or -L (curl).
bash
curl -X POST "$STANDARD_EXPERIENCE_HREF" \
--header 'X-Authentication-Key: YOUR_API_TOKEN' \
-Lpython
anchor = f"#/items/{destination_idx}"
exp_link = next(
l for l in recipients["links"]
if l["rel"].endswith("standard-payment-experience")
and l.get("anchor") == anchor
)
experience = requests.post(exp_link["href"], headers=HEADERS).json()javascript
const anchor = `#/items/${destinationIdx}`;
const expLink = recipients.links.find(
l => l.rel.endsWith("standard-payment-experience") && l.anchor === anchor
);
const experience = await fetch(expLink.href, {
method: "POST", headers: HEADERS, redirect: "follow"
}).then(r => r.json());Option B — set-up-payment-experience
The destination requires additional data before a payment experience can be created.
- GET the
set-up-payment-experiencelink to receive a form with aninputsschema. - Build a JSON body that satisfies the schema.
- POST the body back to the same URL.
- Follow the
303redirect to get the payment experience.
bash
# 1. Get the form
curl "$SETUP_HREF" --header 'X-Authentication-Key: YOUR_API_TOKEN'
# 2. Post the filled form (follow redirect)
curl -X POST "$SETUP_HREF" \
--header 'X-Authentication-Key: YOUR_API_TOKEN' \
--header 'Content-Type: application/json' \
-d '{"amount": {"value": 1500, "currency": "USD"}}' \
-Lpython
anchor = f"#/items/{destination_idx}"
setup_link = next(
l for l in recipients["links"]
if l["rel"].endswith("set-up-payment-experience")
and l.get("anchor") == anchor
)
form = requests.get(setup_link["href"], headers=HEADERS).json()
experience = requests.post(
setup_link["href"],
headers={**HEADERS, "Content-Type": "application/json"},
json={"amount": {"value": 1500, "currency": "USD"}}
).json()javascript
const anchor = `#/items/${destinationIdx}`;
const setupLink = recipients.links.find(
l => l.rel.endsWith("set-up-payment-experience") && l.anchor === anchor
);
const form = await fetch(setupLink.href, { headers: HEADERS })
.then(r => r.json());
const experience = await fetch(setupLink.href, {
method: "POST",
headers: { ...HEADERS, "Content-Type": "application/json" },
body: JSON.stringify({ amount: { value: 1500, currency: "USD" } }),
redirect: "follow"
}).then(r => r.json());WARNING
The inputs schema varies by destination. Always read the form before constructing your POST body.
Step 5 — Get the Payment Link
The payment experience response contains a payment link. This is the URL you share with your client.
json
{
"created": "2026-03-20T14:30:00Z",
"destination": { "name": "Demo University", "country": "US", "route": "tuition" },
"links": [
{ "rel": "tag:flywire.com,2016:agents:api:relation:payment", "href": "https://payment.demo.flywire.com/..." }
]
}TIP
The payment link is a full URL pointing to the Flywire payment portal. The payer opens it in their browser to complete the payment.