JSON Links

For this Pay-By-Link option, you’re creating the link by sending a request to the JSON Link API.

 

How does it work?

  1. Make a request to the API’s base URL.

  2. When the request is successful, the API will respond with the link you can give to your payer.

Base URL

Production
https://gateway.flywire.com/v1/transfers.json
Demo
https://gateway.demo.flywire.com/v1/transfers.json

Authentication

All requests to the JSON Link API need to be authenticated with a special header, called the X-Flywire-Digest header. If you leave out the X-Flywire-Digest header, the request will fail.

Hover to highlight the authentication header

 

curl https://base-url-placeholder/
	-X POST 
	-H "Content-Type: application/json" 
	-H "X-Flywire-Digest:{your_digest}" 
	-d '{
	"provider": "FWU",
...
}

How to generate the X-Flywire-Digest header

1. Convert the JSON body of your request into a string. Use the exact request body you are using for sending the request.

message_body = your_json_object.to_json
string message_body = JsonConvert.SerializeObject(yourJsonObject);
const message_body = JSON.stringify(yourJsonObject);

 

2. Generate the header value:

  1. Encrypt the converted body of your request with your Shared Secret using the SHA-256 algorithm.

  2. Take the result and encrypt it in Base64.

  3. Use the final result as the value for your digest header.

The examples show you how to do this in different programming languages. In each example, exchange the shared_key with your Shared Secret and message_body with the converted JSON body of your request.

digest = OpenSSL::Digest.new('sha256')
encrypted_payload = OpenSSL::HMAC.digest(digest, shared_secret, notification_body)
Base64.encode64(encrypted_payload).strip
			
# Step 1: Define the hashing algorithm to use for the HMAC.
# This initializes the SHA-256 hashing mechanism.
hash_algorithm = OpenSSL::Digest.new('sha256')

# Step 2: Compute the HMAC in binary format.
# This uses the shared secret and the message body to produce the HMAC signature.
hmac_binary = OpenSSL::HMAC.digest(hash_algorithm, shared_secret, message_body)

# Step 3: Encode the HMAC in Base64 for use in the X-Flywire-Digest header.
x_flywire_digest = Base64.encode64(hmac_binary).strip
public static string Digest(string shared_secret, string message_body)
{
    // Step 1: Initialize the HMACSHA256 object with the shared secret.
    // This sets up the hashing algorithm (SHA-256) and the secret key for HMAC.
    using (var hmacsha256 = new HMACSHA256(Encoding.UTF8.GetBytes(shared_secret)))
    {
        // Step 2: Convert the message body into a byte array.
        // The message body is the message payload you want to hash
        var bytes = Encoding.UTF8.GetBytes(message_body);

        // Step 3: Compute the HMAC hash of the message body.
        // This generates the HMAC using the shared secret and the message body.
        var hashedBytes = hmacsha256.ComputeHash(bytes);

        // Step 4: Convert the hashed byte array into a Base64 string.
        return Convert.ToBase64String(hashedBytes);
    }
}
const crypto = require('crypto');

function createDigest(shared_secret, message_body) {
    // Step 1: Initialize the HMAC with the SHA-256 algorithm and the shared secret.
    const hmac = crypto.createHmac('sha256', shared_secret);
    
    // Step 2: Update the HMAC with the message body.
    // The message body is the message payload you want to hash
    hmac.update(message_body);

    // Step 3: Get the HMAC digest and encode it in Base64.
    const digestHeader = hmac.digest('base64');
    
    return digestHeader;  // The `digestHeader` is the X-Flywire-Digest header.
}

Request

Portal settings

These parameters are required to properly direct your payer to your unique PayEx portal.

Your logo in the upper left corner tells your payers that they are in your unique PayEx portal

Amount settings

It depends on your portal settings if you need to use one amount or multiple items, see Portal setting: Amount or items?.

This example uses one total amount.

Passing the amount can be necessary if your portal settings don't allow the payer to edit the amount field, see Portal setting: Can Payers edit the passed amount?

Payer settings

When you pre-fill fields in your PayEx portal, they are usually displayed as read-only for your payer. However, the country field stays editable so payers can choose their preferred country. This flexibility is important because payers may have bank accounts in multiple countries, not just where they live.

The payer's country in your PayEx portal:

Payer information

Payer information fields contain the details of the payer. The information necessary for Flywire to conduct compliance checks, such as Anti-Money-Laundering (AML). If the payer details are known to you, you can pre-fill the payer fields in your PayEx portal.

The payer information fields in your PayEx portal:

When you pre-fill a payer information field, the field will be read-only in your PayEx portal. You can overwrite this behavior and allow your payer to edit pre-filled fields with the allow_to_edit_payer_information parameter.

Dynamic field settings

Fields of a recipient (also called a portal) are fields that are specific to that recipient. Depending on how you use Flywire, you might know them under the names dynamic fields, custom fields, or student fields.

Fields are defined when the recipient (also called portal) is set up by Flywire. They are additional fields that the payer has to fill out when they make their payment (additional to the standard payer fields that are the same for all recipients).

Typically, these are fields your own system needs to be able to process and reconcile the payment. While some fields are common across many recipients, others are unique to yours.

To add, remove or modify your dynamic fields please contact the Solutions team.

dynamic_fields object

Pairs of the id and value of the field:

id

Depends on your portal settings.

You can find out the id of a custom field by checking your portal settings.

value

The value you are passing for this field. For more info see Guide for pre-filling dynamic fields.

Link expiration settings

Language settings for your PayEx portal

Payer redirection settings

You can display a button in your PayEx portal that redirects your payer somewhere, for example your website. This button is displayed on the final page of the payment creation process, to the right of the payment instructions.

Your portal might have default settings for the return button and its label. In that case, you don't need to provide a URL in order to display the button. But if there are no default settings and you don't provide a valid URL in the return_cta parameter, the button will not be displayed

Callback settings

curl https://base-url-placeholder/
-X POST 
-H "Content-Type: application/json" 
-H "X-Flywire-Digest:{your_digest}" 
-d '{
	"provider": "FWU",
	"payment_destination": "Flywireuniversity",
	"amount": 950025,
	"max_amount": 950025,						
	"country": "US",
	"sender_email": "[email protected]",
	"sender_first_name": "John",
	"sender_last_name": "Doe",
	"sender_address1": "1 First St",
	"sender_city": "London",
	"sender_state": "Greater London",
	"sender_zip": "SW1 EN3",
	"sender_phone": "0044123456789",
	"allow_to_edit_payer_information": true,
	"dynamic_fields": {
		"student_id": "123456",
		"student_first_name": "John",
		"student_last_name": "Doe"
		},
	"days_to_expire": "20",
   	"locale": "es-ES",
	"return_cta": "https://www.my-website.com/",
	"return_cta_name": "Return to my website",
	"callback_url": "https://www.my-callback-system.com/",
	"callback_id": "Notifications for Payment 1234",
	"callback_version": "2"
}

Parameters that depend on your portal settings

Payment Amount

Portal setting: Amount or items?

When you set up your portal with Flywire, you decided if you always want to get one total amount from a payer or if you want to differentiate between different items (for example, "housing" and "tuition") that each have their own amount. If you want to change this setting, please contact the Solutions team.

Portal setting: Can Payers edit the passed amount?

When you set up your portal with Flywire, you decided if you want to allow your payers to edit the amount you passed via the generated link. This setting will apply to all links you generate for the portal. If you want to change this setting, please contact the Solutions team.

If my portal does not allow payers to edit the amount, does this mean the amount field in my PayEx portal portal is always read-only for my payers?

No. This setting only applies when a payer followed a link you generated to access your portal. If your portal is accessible through other ways, for example from the Flywire website, the amount field will of course be editable since you didn't pre-fill the field.

 

What your payer sees after following the link:

Dynamic Fields

What are dynamic fields?

Fields of a recipient (also called a portal) are fields that are specific to that recipient. Depending on how you use Flywire, you might know them under the names dynamic fields, custom fields, or student fields.

Fields are defined when the recipient (also called portal) is set up by Flywire. They are additional fields that the payer has to fill out when they make their payment (additional to the standard payer fields that are the same for all recipients).

Typically, these are fields your own system needs to be able to process and reconcile the payment. While some fields are common across many recipients, others are unique to yours.

Notes for pre-filling dynamic fields

When you pre-fill fields via a generated link, dynamic fields will be read-only for your payer.

This means:

  • For checkboxes, your payer won't be able to select more values or un-select the ones you selected.

  • If you pass invalid values (because of a typo or because the internal name of a value has been changed), the field will be empty and read-only for your payer. If the field is mandatory, this can mean that your payer can't complete the payment. Be careful with passing values that are prone to change.

Guide for pre-filling dynamic fields

Text field

A field to enter text. The field can have different formats:

Free text field

Phone number field

Country dropdown field

Date field

When you are checking your portal settings, this field type is called string - text/phone/country/date.

 

How to pass values for the different field formats:

Field name:

Use the internal name (Id) of the field. You can check your portal settings to find the Id.

 

Values:

Free text

Pass the text as a string.

Phone number

The phone number of the payer (including the country code).

Use 00 in front of the country code, for example, "0044123456" for country code 44.

Country

The ISO2 code of the country.

Date

Date in dd/mm/yyyy format, for example 01/01/2025.

"dynamic_fields": {
	"student_id": "ID123456",
	"phone_number": "0044123456789",
	"country_residence": "US",
	"start_date": "01/01/2025"
	}

Single Checkbox

A single checkbox so payers can check it for "yes" or uncheck it for "no"

When you are checking your portal settings, this field type is called boolean.

 

How to pass values for this field type:

Field name:

Use the internal name (Id) of the field. You can check your portal settings to find the Id.

 

Values:

Pass either true (checkbox activated) or false (checkbox deactivated), since this is a boolean field.

"dynamic_fields": {
	"residential_student": "true"
	}

Multiple Checkboxes

Multiple checkboxes so the payer can pick multiple values.

When you are checking your portal settings, this field type is called array.

 

How to pass values for this field type:

Field name:

Use the internal name (Id) of the field. You can check your portal settings to find the Id.

 

Values:

Pass the internal name (value, not the label of the value) as a string.

If you don't know the internal value for the labels in PayEx, contact the Solutions team.

You can pass multiple values as a comma-separated list.
"dynamic_fields": {
	"services_used": "housing, tuition"
	}

Response

If your API request was valid, you'll get a "HTTP 200 - Success" response.

In the url parameter you find the link you generated and that you can give to your payer.

How long can the link be used?

Each JSON link includes a token that determines its expiration:

  1. The link expires after a payment is created with it. After that, the payer can track the payment with a different link.

    or

  2. After the set expiry date (default is 90 days, adjustable via the days_to_expire parameter). The countdown for the expiry date starts immediately after the link is generated.

{ 
"url":"https://payment.flywire.com/orders/4bee6c2c-ae10-465c-ba84-1a978e11681a?token=5e03c2cb-2877-47d8-8226-fbfbf62361b3" 
}

400 Error - Malformed JSON

When does this happen?

The request body is not formatted correctly.

What should you do?

Check the request body for formatting errors, for example missing commas.

{
"errors": ["Malformed JSON"],
"url": "https://www.demo.flywire.com/select-school"
}

401 Error - Unauthorized

When does this happen?

The X-Flywire-Digest header is missing or is invalid.

What should you do?

Make sure you use an X-Flywire-Digest header and generate the digest value correctly, see Authentication.

no content

409 Error - Missing Parameters

When does this happen?

You left out required parameters in the request body.

What should you do?

Include the provider and payment_destination parameters in your request.

{
"errors": ["Payment destination can't be blank"],
"url": "https://www.demo.flywire.com/select-school"
}
{
"errors": ["Provider can't be blank"],
"url": "https://payment.demo.flywire.com?destination=TQQ"
}