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.
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.
Add the script tag to your checkout page:
<head>
<script src="https://public.flute.com/lib/v1.0/flute.mjs" type="module"></script>
</head>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.
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-sessionsProduction Environment:
POST https://api.flute.com/pay-int-api/payment-sessionsA 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:
| Field | Type | Required | Description |
|---|---|---|---|
| amount | number (float) | Yes | Base charge amount. For example, 129.50 for $129.50. |
| mode | string | No | Controls what the payment session is allowed to do. Accepted values: "Payment" (default), "PaymentAndSave", and "SaveMethod" (requires amount: 0). See Operating Modes for details. |
| referenceId | string | No | Merchant-supplied identifier (max 100 characters) for duplicate payment detection. Payments with the same referenceId within the duplicate-detection window are rejected. |
| skipAddressVerification | boolean | No | When 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. |
| tipAmount | number (float) | No | Tip amount charged on top of the base amount. For example, 5.00 for $5.00. Only accepted when tips are enabled for the merchant. |
| customerId | string (UUID) | No | Customer 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". |
| customerHandling | string | No | Controls 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.
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.
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).
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);
}
},
},
});
});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}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:
| Code | Message | Meaning |
|---|---|---|
| 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. |
| 2 | An 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. |
| 3 | Something 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. |
| 4 | The 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. |
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.
After initializing form with a payment session identifier, use form.create() to add the cardholder elements before mounting.
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' },
});| Option | Type | Required | Description |
|---|---|---|---|
| hidden | boolean | No | When 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.firstName | string | Conditional | Cardholder first name. Required when hidden is true. Both firstName and lastName must be provided together. |
| values.lastName | string | Conditional | Cardholder last name. Required when hidden is true. Both firstName and lastName must be provided together. |
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' },
});| Option | Type | Required | Description |
|---|---|---|---|
| hidden | boolean | No | When 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.zipCode | string | Conditional | Postal or ZIP code. Required when hidden is true. |
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.
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}.
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 (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.
customerHandling is only recognized on the v2 payment session endpoint. This field is not supported on v1.
v2 Payment Session Endpoints
| Environment | Endpoint |
|---|---|
| Sandbox | https://sandbox.api.flute.com/v2/payment-sessions |
| Production | https://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.
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',
});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.
| Option | Default Value | Shown 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.',
},
});Once a card is saved, the backend can manage customers and their payment methods through the Customer API.
List your customers:
GET /v1/customersGet a specific customer:
GET /v1/customers/{customerId}List payment methods for a customer:
GET /v1/customers/{customerId}/payment-methodsSee the Customers API Reference for full request or response schemas and pagination options.
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:
| Property | Description |
|---|---|
| formBackgroundColor | Background color of the payment form container. |
| fontFamily | Font family for all text elements. |
| fontSizeBase | Base font size for input fields |
| labelColor | Color of field labels. |
| labelAsteriskColor | Color of the required field asterisk. |
| inputTextColor | Color of text inside input fields. |
| inputBorder | Border style for input fields. |
| inputBackgroundColor | Background color of input fields. |
| inputBorderRadius | Border radius of input fields. |
| inputFocusedBottomBorderColor | Bottom border color when an input is focused. |
| inputErrorBackgroundColor | Background color of input fields in an error state. |
| errorFontSize | Font size for error messages. |
| errorColor | Color of error message text. |
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';
},