Skip to content

Flute Elements

Flute Elements is a drop-in JavaScript library that embeds PCI-compliant payment fields directly into your checkout page. It lets you collect card details without sensitive data ever touching your servers, as the embedded elements are securely rendered and managed by Flute's infrastructure.

Flute Elements renders secure, isolated payment input fields, such as card number, expiration period, and CVV, inside your page using iframes managed by the flute.mjs library. This means:

  • PCI-DSS compliance. Card data is captured and transmitted through Flute's secure infrastructure, not your servers.
  • Custom user experience. You control the flow after submission, either by handling the result inline with a callback or redirecting the customer to a custom result page.
  • Full styling control. Customize fonts, colors, borders, and layout to match your branding.

Integration Overview

Flute APIMerchant BackendMerchant FrontendCustomerFlute APIMerchant BackendMerchant FrontendCustomerPOST /pay-int-api/payment-sessions1{ "id": "SESSION_ID" }2SESSION_ID3Initialize Flute Elements (session ID)4Renders secure card fields5Fills in card details and submits6flute.submit()7submission_callback()8Fetch session result9GET /pay-int-api/payment-sessions/{id}10Session object11Session result12Show result13
Flute APIMerchant BackendMerchant FrontendCustomerFlute APIMerchant BackendMerchant FrontendCustomerPOST /pay-int-api/payment-sessions1{ "id": "SESSION_ID" }2SESSION_ID3Initialize Flute Elements (session ID)4Renders secure card fields5Fills in card details and submits6flute.submit()7submission_callback()8Fetch session result9GET /pay-int-api/payment-sessions/{id}10Session object11Session result12Show result13

Building with Flute Elements

The integration with Flute Elements follows three steps:

  • Step 1: Loading the flute.mjs Library into your checkout page.
  • Step 2: Building the Payment Form using a payment session identifier from the backend.
  • Step 3: Submitting the Form and retrieving the result from the backend.

Step 1: Loading the flute.mjs Library

Add the script tag to your checkout page in both the sandbox and production environments.

<head>
  <script src="https://public.flute.com/lib/v1.0/flute.mjs" type="module"></script>
</head>

Step 2: Building the Payment Form

To build the payment form, create a payment session from the backend and initialize the Flute Elements form on the frontend using the payment session identifier.

Creating a Payment Session

The backend must create a payment session before initializing the Flute Elements form. To create the payment session, call the Create payment session endpoint from the backend.

Sandbox Environment:

POST https://sandbox.api.flute.com/pay-int-api/payment-sessions

Production Environment:

POST https://api.flute.com/pay-int-api/payment-sessions

A merchant API token is needed when creating payment sessions. For more information, see Generating an API Token in the API reference guide.

Request Body Parameters:

FieldTypeRequiredDescription
amountnumber
(float)
YesBase charge amount.

For example, 129.50 for $129.50.
modestringNoControls what the payment session is allowed to do.

Accepted values: "Payment" (default), "PaymentAndSave", and "SaveMethod" (requires amount: 0). See Operating Modes for details.
referenceIdstringNoMerchant-supplied identifier (max 50 characters) for duplicate payment detection.

Payments with the same referenceId within the duplicate-detection window are rejected.
skipAddressVerificationbooleanNoSpecifies skipping the AVS (address verification service).

If true, AVS checks are skipped.
If false, AVS checks are enabled and the form must include the address element.

The default is false. See Address Verification Service (AVS) for details.
tipAmountnumber
(float)
NoTip amount charged on top of the base amount.

For example, 5.00 for $5.00. Only accepted when tips are enabled for the merchant.
customerIdstring (UUID)NoCustomer identifier to associate saved payment methods with.


Only relevant for SaveMethod and PaymentAndSave payment sessions.

If null or omitted, a new customer is created automatically. Retrieve the resulting customerId through GET /pay-int-api/payment-sessions/{paymentSessionId}.
If provided, the new payment method is saved under that existing customer.

This cannot be combined with a customerHandling: "TokenOnly".
customerHandlingstringNoSpecifies a saved payment method is attached to a customer record.

This is recognized only on the v2 payment session endpoint.

Accepted values:
* CreateCustomer (default): A Customer record is auto-created (or reused if one already exists), and the vaulted payment method is attached to it.
* TokenOnly: This saves the payment method as a bare token with no customer record. This is relevant only for SaveMethod and PaymentAndSave payment sessions. This value cannot be used with a customerId value.
* If null or omitted means no customer handling preference is specified.

See Standalone tokenization (no customer record).
taxRatenumber
(float)
NoTax rate percentage to apply to the base amount, from 0 to 100 with up to three decimal places.

This is recognized only on the v2 payment session endpoint.

Must be set together with taxMode, or both omitted. If both are omitted, the payment session has no tax configured.
taxModestringNoSpecifies whether taxRate is applied on top of amount or already included in it.

This is recognized only on the v2 payment session endpoint.

Accepted values:
* Exclusive: Adds the tax on top of amount.
* Inclusive: Treats amount as already including the tax.

Must be set together with taxRate, or both omitted.

The following is a sample request for the sandbox environment.

curl -X POST 'https://sandbox.api.flute.com/pay-int-api/payment-sessions' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer <ACCESS_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{
    "amount": 129.50,
    "mode": "PaymentAndSave",
    "referenceId": "order-8675309",
    "skipAddressVerification": false,
    "tipAmount": 5.00,
    "customerId": "123e4567-e89b-12d3-a456-426614174000"
  }'

The following is a sample partial response from the previous call. The id value is the paymentSessionsId.

{
  "id": "0cdddcb6-fa98-4c46-b096-a2723b56c750",
  ...
}

The payment session endpoint response contains the payment session identifier in the id field. The frontend uses this value to initialize the form.

Adding the Form Wrapper

Add a wrapper element that will contain the Flute Elements embedded form. You can also add a submit button or use your existing checkout button to trigger the form submission.

<div id="flute-payment-form"></div>
<button id="flute-payment-submit-btn">Pay</button>

Flute Elements will render the payment fields inside the #flute-payment-form wrapper element. The tag IDs (#flute-payment-form and #flute-payment-submit-btn) can be customized, but they must match the IDs used in the mounting and event listener steps below.

Mounting the Flute Elements Form

Pass the payment session identifier (the id field returned by the backend) to Flute Elements, create the customer name, payment, and address elements, and mount them together into the wrapper element.

const flute = new window.Flute();

const form = flute.elements({
  sessionId: 'SESSION_ID', // Payment session identifier from the backend.
  appearance: {},          // Optional. See Customizing the Appearance below.
});

form.create('customer-name');
form.create('payment');
form.create('address'); // Required while AVS is enabled (the default): collects the ZIP code that AVS verifies.

form.mount('#flute-payment-form'); // Identifier of the wrapper element in your page.

The address element, which includes zipCode and streetAddress, is required while AVS is enabled. This is the default for every payment session (skipAddressVerification: false). Without it, AVS has nothing to verify and the payment is declined. To hide or pre-fill the customer-name and address fields when you collect this information elsewhere in your checkout, see Address Verification Service (AVS).

Step 3: Submitting the Form

Attach a click handler to your submit button.

const submitButton = document.getElementById('flute-payment-submit-btn');

submitButton.addEventListener('click', () => {
  flute.submit({
    confirmParams: {
      submission_callback: () => {
        // Payment interaction complete.
        // Call the backend to `GET /pay-int-api/payment-sessions/{flute_session_id}`
        // to retrieve the transaction result.
      },
      error_callback: (message, code) => {
        alert(message);
        // If code is 3 or 4, generate a new payment session from the backend and refresh the form.
        if (code === 3 || code === 4) {
          const newSessionId = 'NEW_SESSION_ID_FROM_BACKEND';
          flute.updateSessionId(newSessionId);
        }
      },
    },
  });
});

Retrieving the Session Result

Always retrieve the payment result from the backend. Never trust the frontend for payment session status or results, as it can be spoofed.

After the customer completes the form submission, the backend must call the payment session endpoint to get the actual status and details. This will return one of the following payment session statuses: Created, Completed, Cancelled, Expired, or Failed. The response will also contain more information about the payment session, such as transaction or customer details.

GET /pay-int-api/payment-sessions/{flute_session_id}

Error Handling

When a payment submission fails, the error_callback receives a message and a numeric error code. For error codes 1 and 2, the payment session has not been consumed and it can be retried. For error codes 3 and 4, the payment session is consumed and cannot be reused. In this case, generate a new payment session from the backend and call flute.updateSessionId(newSessionId) to retry.

Error Codes:

CodeMessageMeaning
1Invalid form data. Please review and try again.
Something went wrong. Check payment details and try again.
Validation error. The payment session remains open for new payment attempts. Two cases, in the same order as the messages:
• Form validation — a required field is empty or invalid, so nothing was submitted to the gateway. Prompt the customer to correct the highlighted fields and submit again.
• Gateway validation — the payment was not submitted due to an invalid BIN (card number) or a duplicate transaction detected within a short time frame.
2An integration problem has been detected. Contact the administrator.Integration or server-side error. The payment session stays open for retry. We recommend reviewing the payload data and mounted elements before retrying.
3Something went wrong, check the payment session identifier and try again.Invalid or expired payment session identifier. Generate a new payment session from your backend and update the form.
4The payment could not be completed. Check payment details and try again.Transaction declined, such as insufficient funds, an incorrect CVV, or missing or failed AVS data (for example, no ZIP code was collected while AVS is enabled; see Address Verification Service (AVS)). Generate a new payment session to retry.

Address Verification Service (AVS)

AVS (address verification service) is a system that verifies the cardholder's billing address with the card issuer. It attempts to reduce fraud and improve transaction approval rates.

Because AVS is enabled by default (skipAddressVerification: false), the quickstart form in Step 2 already includes the customer-name and address elements alongside the payment element, with no extra setup needed to satisfy AVS. This section describes how to adapt those elements when you collect the cardholder's name or billing address elsewhere in your checkout. Each element supports a hidden option to submit values without rendering the fields (hidden: true) and a values option to pre-fill them.

Creating the Elements

After initializing form with a payment session identifier, use form.create() to add the cardholder elements before mounting.

Customer Name Element

Collects the cardholder's first and last name.

// Visible: fields are shown empty for the customer to fill in
form.create('customer-name');

// Visible and pre-filled: fields are shown pre-filled; the customer can edit them
form.create('customer-name', {
  values: { firstName: 'John', lastName: 'Doe' },
});

// Hidden: values are submitted without displaying the fields
form.create('customer-name', {
  hidden: true,
  values: { firstName: 'John', lastName: 'Doe' },
});
OptionTypeRequiredDescription
hiddenbooleanNoWhen true, the fields are not rendered in the UI. When false (or omitted), values acts as optional defaults the customer can override. Defaults to false.
values.firstNamestringConditionalCardholder first name. Required when hidden is true. Both firstName and lastName must be provided together.
values.lastNamestringConditionalCardholder last name. Required when hidden is true. Both firstName and lastName must be provided together.

Address Element

Collects the cardholder's billing address for AVS (address verification service) verification.

// Visible: field is shown pre-filled; the customer can edit it
form.create('address', {
  values: { streetAddress: '35 S. North Street', zipCode: '12345' }
});

// Hidden: value is submitted without displaying the field
form.create('address', {
  hidden: true,
  values: { streetAddress: '35 S. North Street', zipCode: '12345' }
});
OptionTypeRequiredDescription
hiddenbooleanNoWhen true, the field is not rendered in the UI. When false (or omitted), values acts as the default input value, which the customer can override. Defaults to false.
values.zipCodestringConditionalPostal or ZIP code. Required when hidden is true.
values.streetAddressstringConditionalStreet address. Required when hidden is true.

Mounting All Elements

After creating the payment, customer-name, and address elements, mount them all into the wrapper element. You can create the elements in any order — the order you create them in determines the order they're rendered in the form (see below).

const flute = new window.Flute();

const form = flute.elements({
  sessionId: 'SESSION_ID',
  appearance: {},
});

form.create('customer-name');
form.create('payment');
form.create('address');

form.mount('#flute-payment-form');

Elements are rendered in the order they are created. If address is created before payment, the address fields will appear above the payment fields in the form. Adjust the order of your form.create() calls to control the layout.

Saving Payment Methods

Flute Elements can save a customer's card for future payments, either as a standalone operation or alongside a payment. The backend controls the behavior through the mode field at payment session creation, and the frontend through the saveMethod option on the payment element.

When saving a card, Flute can either create and manage a customer record on your behalf (the default), or return a standalone payment method token with no customer record attached. After the customer submits the form, retrieve the vaultedPaymentMethodId from the backend and store it to charge the same card again in future transactions without re-collecting the card details.

To manage saved cards and the customers they belong to, see the Customers API Reference, specifically the cards response object on GET /pay-api/v1/customers/{customerId}.

Operating Modes

The mode field (set when creating the payment session on the backend) and saveMethod (set on the frontend element) must be used together. The table below shows the valid combinations and their behavior.

mode (backend)saveMethod (frontend)Behavior
"Payment" (default)(not applicable)Processes the payment only. Card is not stored.
"SaveMethod""vaultOnly"Saves the card without charging. A disclosure notice is shown. If no customerId is provided, a new customer record is created automatically.
"PaymentAndSave""askConsent"Processes the payment. A checkbox lets the customer opt in to saving their card.
"PaymentAndSave""implicit"Processes the payment and always saves the card. A disclosure notice is shown.

Payment Session Setup

Payment session (default)

curl -X POST '.../pay-int-api/payment-sessions' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "amount": 129.50
  }'

Payment with customer vaulting

curl -X POST '.../pay-int-api/payment-sessions' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "amount": 129.50,
    "mode": "PaymentAndSave"
  }'

Save customer and method only (no payment)

curl -X POST '.../pay-int-api/payment-sessions' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "amount": 0,
    "mode": "SaveMethod",
    "customerId": "CUSTOMER_ID"
  }'

For save-only payment sessions, the amount must be 0. Optionally provide a customerId to attach the card to an existing customer record. If omitted, a new customer record is created automatically. The resulting customerId can be retrieved with GET /pay-int-api/payment-sessions/{paymentSessionId}.

Standalone tokenization (no customer record)

If you manage your own customer identities and don't want Flute to create or store a customer record, set customerHandling to TokenOnly. This is available for both SaveMethod and PaymentAndSave modes and cannot be combined with customerId.

Note

customerHandling is only recognized on the v2 payment session endpoint. This field is not supported on v1.

taxRate and taxMode are also recognized only on the v2 payment session endpoint.

v2 Payment Session Endpoints

EnvironmentEndpoint
Sandboxhttps://sandbox.api.flute.com/v2/payment-sessions
Productionhttps://api.flute.com/v2/payment-sessions
curl -X POST 'https://sandbox.api.flute.com/v2/payment-sessions' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "amount": 0,
    "mode": "SaveMethod",
    "customerHandling": "TokenOnly"
  }'

The resulting payment session always has customerId: null. Retrieve the token through vaultedPaymentMethodId from GET /pay-int-api/payment-sessions/{paymentSessionId}. If the cardholder name and/or address elements are submitted, they're used for AVS verification. They are not stored on any customer record.

Frontend Element Setup for Saving

Set saveMethod when calling form.create('payment', options):


// For Session mode "PaymentAndSave":

// Payment with optional vaulting: shows an opt-in checkbox
const paymentElement = form.create('payment', {
  saveMethod: 'askConsent',
});

// Payment with mandatory vaulting: shows a disclosure
const paymentElement = form.create('payment', {
  saveMethod: 'implicit',
});

// For Session mode "SaveMethod":

// Save-only: shows a disclosure, no payment is processed
const paymentElement = form.create('payment', {
  saveMethod: 'vaultOnly',
});

Label Overrides

When using askConsent, implicit, or vaultOnly save methods, Flute Elements displays a checkbox or disclosure to inform the customer about saving their card.

The checkbox label (for "askConsent") and the disclosure label (for "implicit" and "vaultOnly") can be overridden with the labels option. Values must not be empty strings.

OptionDefault ValueShown for
labels.saveConsent"Save this card for future payments."saveMethod: "askConsent"
labels.disclosure"This card will be saved for future payments."saveMethod: "implicit" and saveMethod: "vaultOnly"

Example with custom checkbox label:

const paymentElement = form.create('payment', {
  saveMethod: 'askConsent',
  labels: {
    saveConsent: 'Remember my card for next time.',
  },
});

Managing Customers and Saved Cards

Once a card is saved, the backend manages customers and their payment methods through the Customer API endpoints.

List your customers:

GET /pay-api/v1/customers

Get a specific customer:

GET /pay-api/v1/customers/{customerId}

List payment methods for a customer:

GET /pay-api/v1/customers/{customerId}/payment-methods

For full request and response schemas and pagination options, see the Customers API Reference.

Customizing the Appearance

Flute Elements supports custom theming through the appearance option to make the form match your brand and design.

const form = flute.elements({
  sessionId: sessionId,
  appearance: {
    elements: {
      formBackgroundColor: '#e1e9eb',
      fontFamily: 'Verdana, sans-serif',
      fontSizeBase: '16px',
      labelColor: '#107b92',
      labelAsteriskColor: '#0000ff',
      inputTextColor: '#107b92',
      inputBorder: 'none',
      inputBackgroundColor: '#c8d6d9',
      inputBorderRadius: '6px',
      inputFocusedBottomBorderColor: '#107b92',
      inputErrorBackgroundColor: '#ecc9c9',
      errorFontSize: '14px',
      errorColor: '#cd2424',
    },
  },
});

Available Appearance Properties:

PropertyDescription
formBackgroundColorSets the background color of the payment form container.
fontFamilySets the font family for all text elements.
fontSizeBaseSets the base font size for input fields.
labelColorSets the color of field labels.
labelAsteriskColorSets the color of the required field asterisk.
inputTextColorSets the color of text inside input fields.
inputBorderSets the border style for input fields.
inputBackgroundColorSets the background color of input fields.
inputBorderRadiusSets the border radius of input fields.
inputFocusedBottomBorderColorSets the bottom border color when an input is focused.
inputErrorBackgroundColorSets the background color of input fields in an error state.
errorFontSizeSets the font size for error messages.
errorColorSets the color of error message text.

Migration Notes

If you have an existing Flute Elements integration, the following methods and parameters have been deprecated.

flute.confirmPayment() is deprecated. Use flute.submit() instead. The interface is identical: replace the method name and no other changes are needed.

return_url is deprecated. Use submission_callback instead and redirect manually when needed:

submission_callback: () => {
  window.location.href = '/your-success-page';
},