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, expiry, and CVV, inside your page using iframes managed by the flute.js 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.js 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.js Library

Add the script tag to your checkout page:

<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 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 100 characters) for duplicate payment detection. Payments with the same referenceId within the duplicate-detection window are rejected.
skipAddressVerificationbooleanNoWhen false (the default), AVS checks are enabled and the form must include the address element. When true, AVS checks are skipped. 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 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.
Cannot be combined with customerHandling: "TokenOnly".
customerHandlingstringNoControls whether a saved payment method is attached to a customer record. Only relevant for SaveMethod and PaymentAndSave payment sessions. Accepted values:
"CreateCustomer" (default): a customer is auto-created or reused.
"TokenOnly": saves the payment method as a bare token with no customer record. Cannot be combined with customerId. See Standalone tokenization (no customer record).
This is recognized only on the v2 payment session endpoint.

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 response from the previous call.

{
  "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 (ZIP code) element is required while AVS is enabled, which 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, 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
1• Invalid 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 recommended 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 act 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: { zipCode: '12345' },
});

// Hidden: value is submitted without displaying the field
form.create('address', {
  hidden: true,
  values: { 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.

Mounting All Elements

After creating the payment, customer-name, and address elements, mount them all into a single secure iframe. You can create the elements in any order.

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 /v2/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.

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 can manage customers and their payment methods through the Customer API.

List your customers:

GET /v1/customers

Get a specific customer:

GET /v1/customers/{customerId}

List payment methods for a customer:

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

See the Customers API Reference for full request or response schemas and pagination options.

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
formBackgroundColorBackground color of the payment form container.
fontFamilyFont family for all text elements.
fontSizeBaseBase font size for input fields
labelColorColor of field labels.
labelAsteriskColorColor of the required field asterisk.
inputTextColorColor of text inside input fields.
inputBorderBorder style for input fields.
inputBackgroundColorBackground color of input fields.
inputBorderRadiusBorder radius of input fields.
inputFocusedBottomBorderColorBottom border color when an input is focused.
inputErrorBackgroundColorBackground color of input fields in an error state.
errorFontSizeFont size for error messages.
errorColorColor 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';
},