> ## Documentation Index
> Fetch the complete documentation index at: https://docs.loqate.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Bank Validation Implementation Guide

> Add bank account validation to your website with copy-paste code examples

export const GistEmbed = ({url, height = "500px"}) => {
  const [htmlContent, setHtmlContent] = useState('');
  const [copied, setCopied] = useState(false);
  const [rawCode, setRawCode] = useState('');
  const [isDark, setIsDark] = useState(true);
  useEffect(() => {
    const checkTheme = () => {
      const htmlElement = document.documentElement;
      const computedStyle = window.getComputedStyle(htmlElement);
      const colorScheme = computedStyle.getPropertyValue('color-scheme');
      const isDarkMode = htmlElement.classList.contains('dark') || htmlElement.getAttribute('data-theme') === 'dark' || colorScheme.includes('dark') || window.matchMedia('(prefers-color-scheme: dark)').matches;
      setIsDark(isDarkMode);
    };
    checkTheme();
    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
    mediaQuery.addEventListener('change', checkTheme);
    const observer = new MutationObserver(checkTheme);
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class', 'data-theme', 'style']
    });
    return () => {
      mediaQuery.removeEventListener('change', checkTheme);
      observer.disconnect();
    };
  }, []);
  useEffect(() => {
    fetch(url).then(r => r.text()).then(code => {
      setRawCode(code);
      const bgColor = isDark ? '#0a0c0e' : '#ffffff';
      const textColor = isDark ? '#c9d1d9' : '#24292e';
      const scrollTrack = isDark ? '#161b22' : '#f6f8fa';
      const scrollThumb = isDark ? '#30363d' : '#d1d5da';
      const scrollThumbHover = isDark ? '#484f58' : '#959da5';
      const iframe = `
          <!DOCTYPE html>
          <html>
            <head>
              <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
              <style>
                body {
                  margin: 0;
                  padding: 0;
                  background: ${bgColor};
                  overflow: auto;
                }
                pre {
                  margin: 0;
                  background: transparent !important;
                }
                code {
                  font-family: 'JetBrains Mono', JetBrains Mono, monospace !important;
                  font-size: 14px !important;
                  line-height: 1.7 !important;
                  background: transparent !important;
                  padding: 0 !important;
                }
                .hljs {
                  background: transparent !important;
                  color: ${textColor} !important;
                }
                ::-webkit-scrollbar {
                  width: 12px;
                  height: 12px;
                }
                ::-webkit-scrollbar-track {
                  background: ${scrollTrack};
                  border-radius: 10px;
                }
                ::-webkit-scrollbar-thumb {
                  background: ${scrollThumb};
                  border-radius: 10px;
                  border: 2px solid ${scrollTrack};
                }
                ::-webkit-scrollbar-thumb:hover {
                  background: ${scrollThumbHover};
                }
                * {
                  scrollbar-width: thin;
                  scrollbar-color: ${scrollThumb} ${scrollTrack};
                }
              </style>
            </head>
            <body>
              <pre><code class="language-html">${escapeHtml(code)}</code></pre>
              <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
              <script>hljs.highlightAll();</script>
            </body>
          </html>
        `;
      setHtmlContent(iframe);
    }).catch(e => {
      setRawCode('Error loading code');
      setHtmlContent('<pre>Error loading code</pre>');
    });
  }, [url, isDark]);
  const escapeHtml = text => {
    return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;');
  };
  const copyToClipboard = () => {
    navigator.clipboard.writeText(rawCode);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };
  const containerBg = isDark ? '#0a0c0e' : '#ffffff';
  const containerBorder = isDark ? '#30363d' : '#e1e4e8';
  return <div style={{
    position: 'relative',
    marginBottom: '1.5rem',
    marginTop: '1.5rem',
    background: containerBg,
    borderRadius: '12px',
    border: `1px solid ${containerBorder}`,
    padding: '16px 20px'
  }}>
      <button onClick={copyToClipboard} style={{
    position: 'absolute',
    top: '12px',
    right: '32px',
    padding: '8px',
    background: 'rgba(255, 255, 255, 0.08)',
    backdropFilter: 'blur(4px)',
    border: '1px solid rgba(255, 255, 255, 0.1)',
    borderRadius: '6px',
    cursor: 'pointer',
    fontSize: '13px',
    fontWeight: '500',
    color: 'rgba(255, 255, 255, 0.8)',
    zIndex: 10,
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    transition: 'all 0.15s',
    fontFamily: 'system-ui, -apple-system, sans-serif',
    width: '32px',
    height: '32px'
  }} onMouseEnter={e => {
    e.currentTarget.style.background = 'rgba(255, 255, 255, 0.12)';
  }} onMouseLeave={e => {
    e.currentTarget.style.background = 'rgba(255, 255, 255, 0.08)';
  }}>
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          {copied ? <polyline points="20 6 9 17 4 12" /> : <>
              <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
              <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
            </>}
        </svg>
      </button>
      <iframe srcDoc={htmlContent} style={{
    width: '100%',
    height: height,
    border: 'none',
    overflow: 'auto',
    background: 'transparent'
  }} title="Code Example" />
    </div>;
};

**Reduce failed payments and fraud by validating UK bank account details at checkout or in payment forms.**

## Who Should Use This Guide

**For:** Business owners and developers integrating bank validation without complex setup.

**You'll need:** A Loqate API key and basic HTML knowledge.

**Not covered:** Tag-based integrations ([see Tag Setup](/our-services/tag-setup-guide)) or pre-built platform integrations ([see Integrations](/integrations/all-integrations)).

## What You'll Build

A bank validation form that:

* Validates sort codes and account numbers in real-time
* Displays success or error states
* Prevents form submission with invalid bank details
* Shows bank and branch information

## Quick Start

To set up Bank Validation, you'll need an API key. Find out how to get yours [here](/loqate-basics/create-an-api-key/).

### Choose Your Approach

<Tabs>
  <Tab title="Standalone Form">
    **Use case:** Testing the API or adding to a static page

    A self-contained HTML file you can copy and use immediately.

    Jump to [standalone HTML example](#standalone-html-example)
  </Tab>

  <Tab title="Integrate with Existing Form">
    **Use case:** Add validation to your current checkout or payment forms

    Step-by-step guide to add validation code to forms you already have.

    Jump to [integration example](#integration-example)
  </Tab>
</Tabs>

**Using Shopify or WordPress?** Check [pre-built integrations](/integrations/all-integrations).

### Add Your Key

Replace `YOUR_API_KEY` in the code with your actual Loqate API key.

### Test

1. Open your page
2. Enter sort code `40-47-84` and account number `70872490`
3. Validation result appears

<Note>
  Examples validate UK bank accounts. For international accounts (IBAN), see the [International Validate endpoint](/api-reference/bank-validation/international).
</Note>

***

## Sample Project: Payment Form Integration

This walkthrough shows how to add bank validation to a payment form. Follow along to see a basic form transform into a validation-enabled payment form.

<Note>
  This is a detailed walkthrough. If you just want working code, skip to [Integration Example](#integration-example) or [Standalone HTML Example](#standalone-html-example).
</Note>

### Before: Basic Payment Form

A payment form without validation:

<GistEmbed url="https://raw.githubusercontent.com/loqate/javascript-docs-sample/refs/heads/main/bank-validation/start/basic-checkout-form.html" height="450px" />

**This form works, but doesn't validate bank details.** Let's add validation.

***

## How Bank Validation Works

The integration includes four key components:

### 1. Validation Function

A JavaScript function that sends sort codes and account numbers to the Loqate API and returns validation results. The function handles API requests, error checking, and response parsing.

### 2. Visual Feedback

CSS classes and a message element that show users three states:

* **Checking:** Yellow background while validating
* **Valid:** Green background for valid bank details
* **Invalid:** Red background for invalid or mismatched details

### 3. Real-Time Validation

Event listeners on both sort code and account number fields that trigger validation when users finish typing (on blur). This provides immediate feedback without requiring form submission.

### 4. Form Submission Control

Logic that prevents form submission if bank details are invalid, ensuring only validated payment information is processed.

***

## Standalone HTML Example

A minimal bank validation form for quick testing:

<GistEmbed url="https://raw.githubusercontent.com/loqate/javascript-docs-sample/refs/heads/main/bank-validation/finish/simple-bank-validation.html" height="450px" />

**Perfect for:**

* Quick API testing
* Learning how bank validation works
* Simple integrations without checkout flows

**To use:**

1. Copy the code
2. Replace `YOUR_API_KEY`
3. Save and test with different sort codes and account numbers

<Note>
  Test with UK sort codes only. For IBAN validation, use the [International Validate endpoint](/api-reference/bank-validation/international).
</Note>

## Code on GitHub

If you'd like to browse the code snippets in this implementation guide or clone them, visit the repository on [GitHub](https://github.com/loqate/javascript-docs-sample/tree/main/bank-validation).

## Integration Example

Here's the payment form with all validation components integrated:

<GistEmbed url="https://raw.githubusercontent.com/loqate/javascript-docs-sample/refs/heads/main/bank-validation/finish/complete-checkout-with-bank-validation.html" height="700px" />

**To use this code:**

1. Copy the code
2. Replace `YOUR_API_KEY` with your actual Loqate API key
3. Save as an HTML file
4. Open in a browser to test
5. Customize styling to match your brand

**Key features in this example:**

* Real-time validation on field exit
* Visual feedback for all validation states
* Form submission prevention for invalid details
* Bank and branch information display
* Clean, production-ready code structure

<Note>
  This example validates UK bank accounts only. The sort code must be 6 digits (with or without hyphens) and the account number must be 8 digits.
</Note>

***

## Integration Tips

### Adding to Your Existing Forms

To add bank validation to your current forms:

1. **Add the validation function** from the integration example to your page's `<script>` section
   * Handles API requests and response parsing

2. **Add validation styles** from the integration example to your page's `<style>` section
   * Provides visual feedback (valid/invalid/checking states)

3. **Add the validation message element** next to your bank details fields
   * `<span id="bankValidation"></span>`

4. **Add the event listeners** to trigger validation and control form submission
   * Blur events for real-time validation on both fields
   * Submit event to prevent invalid submissions

Reference the [integration example](#integration-example) above for all the code you need.

### Sort Code Formatting

The API accepts sort codes with or without hyphens:

* `404784` (6 digits, no hyphens)
* `40-47-84` (with hyphens)

Both formats are valid. The API returns the sort code in 6-digit format without hyphens.

**Auto-format user input:**

```javascript theme={null}
function formatSortCode(value) {
    // Remove non-digits
    const digits = value.replace(/\D/g, '');
    
    // Add hyphens: 40-47-84
    if (digits.length >= 2) {
        return digits.slice(0, 2) + '-' + 
               digits.slice(2, 4) + '-' + 
               digits.slice(4, 6);
    }
    return digits;
}

sortCodeInput.addEventListener('input', function(e) {
    e.target.value = formatSortCode(e.target.value);
});
```

### Account Number Formatting

Account numbers must be exactly 8 digits. Pad shorter numbers with leading zeros:

```javascript theme={null}
function formatAccountNumber(value) {
    const digits = value.replace(/\D/g, '');
    return digits.padStart(8, '0').slice(0, 8);
}
```

### Validation Timing Options

**Current examples use:** Validation on field exit (blur event) - validates when users click or tab away from either field.

**Alternative approaches:**

**On button click:**
Add a separate "Validate" button that triggers validation manually.

**After both fields complete:**
Validate only after both sort code and account number are entered.

**On form submission only:**
Validate only when the user submits the form, showing errors before processing.

### Handling StatusInformation

The `StatusInformation` field provides details about validation results:

| Status            | Meaning                       | Action                                               |
| ----------------- | ----------------------------- | ---------------------------------------------------- |
| `OK`              | Details are valid             | Accept and process                                   |
| `DetailsChanged`  | Corrected automatically       | Use `CorrectedSortCode` and `CorrectedAccountNumber` |
| `CautiousOK`      | Valid but no validation rules | Accept with caution (rare)                           |
| `UnknownSortCode` | Sort code not found           | Reject the details                                   |

**Example handling:**

```javascript theme={null}
const result = await validateBank(sortCode, accountNumber);

if (result.IsCorrect === "Yes" || result.IsCorrect === true) {
    if (result.StatusInformation === "DetailsChanged") {
        // Use corrected values
        sortCodeInput.value = result.CorrectedSortCode;
        accountInput.value = result.CorrectedAccountNumber;
        showSuccess("Bank details corrected and validated");
    } else {
        showSuccess("Bank details validated");
    }
} else {
    showError("Invalid bank details");
}
```

### Displaying Bank Information

Show users their bank and branch information for confirmation:

```javascript theme={null}
if (result.Bank && result.Branch) {
    const bankInfo = `${result.Bank} - ${result.Branch}`;
    document.getElementById('bankInfo').textContent = bankInfo;
}
```

**Note:** Branch addresses may show central processing centers, not physical branches. This is normal for BACS operations.

### Styling Customization

The examples use minimal styling. Customize the CSS classes (`.bank-valid`, `.bank-invalid`, `.bank-checking`) to match your brand:

* Change colors to match your design system
* Adjust padding and border-radius
* Add icons or animations
* Modify font sizes and weights

***

## Troubleshooting

### Invalid API Key

Verify you've replaced `YOUR_API_KEY` with your actual key from your Loqate account.

### Bank Details Not Validating

**Check:**

* Sort code is exactly 6 digits (with or without hyphens)
* Account number is exactly 8 digits
* You have available credits in your account
* API key hasn't been restricted by domain, IP, or rate limits (check account settings)
* You're validating UK bank accounts (not international IBANs)

### Unknown Sort Code Errors

If you receive `UnknownSortCode`:

* Verify the sort code is correct (check with the bank)
* Ensure it's a valid UK sort code
* Some newer or specialized banks may not be in the database

### Form Not Appearing

Ensure you copied the standalone HTML file including all `<style>` and `<script>` sections. The examples are self-contained and include all required code.

### CORS Errors

The examples must be hosted on a web server, not opened as local `file://` URLs. Upload to your website or use a development server like Python's `http.server` or Node's `http-server`.

***

## Data Privacy

* Bank details validated through Loqate's API
* Stored in infrastructure logs for 30 days for operational purposes
* Results returned immediately
* See [Privacy Policy](https://www.loqate.com/en-gb/legal/privacy-policy/) for complete details

**Important:** Bank Validation does not return account holder information. Only sort code and account number validity are verified.

***

## Support

* [API Reference](/api-reference/bank-validation/individual)
* [Contact](https://www.loqate.com/en-gb/contact/customer-support/)

***

## Related

* [API Security](/loqate-basics/api-security)
* [Pricing](https://www.loqate.com/en-gb/pricing/)
