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

# Yield-as-a-Service integration

> Technical integration for Yield-as-a-Service: provision a sub-account per end user, fund it, run the Crypto Lending lifecycle in each sub-account, and reconcile earnings per user.

export const RegionBaseUrl = ({className = "", showBaseUrl = true, protocol = "https"}) => {
  const {useState, useEffect, useRef} = React;
  const EU_ENABLED = false;
  const [region, setRegionState] = useState(() => {
    if (!EU_ENABLED) return "com";
    if (typeof window !== 'undefined') {
      return localStorage.getItem("api-region-preference") || "com";
    }
    return "com";
  });
  const [mounted, setMounted] = useState(false);
  const observerRef = useRef(null);
  const isSyncingRef = useRef(false);
  const updateAllContentOnPage = targetRegion => {
    try {
      const domainFrom = targetRegion === "eu" ? "whitebit.com" : "whitebit.eu";
      const domainTo = targetRegion === "eu" ? "whitebit.eu" : "whitebit.com";
      const links = document.querySelectorAll('a');
      links.forEach(link => {
        let href = link.getAttribute('href');
        if (href && href.includes(domainFrom)) {
          link.setAttribute('href', href.replace(domainFrom, domainTo));
        }
      });
      const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {
        acceptNode: node => {
          if (node.parentElement?.closest('.region-toggle-component')) {
            return NodeFilter.FILTER_REJECT;
          }
          return node.textContent.includes(domainFrom) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
        }
      });
      let currentNode;
      while (currentNode = walker.nextNode()) {
        currentNode.textContent = currentNode.textContent.replace(new RegExp(domainFrom, 'g'), domainTo);
      }
      console.log(`[RegionSync] Global content updated to ${domainTo}`);
    } catch (e) {
      console.error("[RegionSync] Error updating content:", e);
    }
  };
  const updateRegion = (newRegion, source) => {
    if (region === newRegion) return;
    console.log(`[RegionBaseUrl] Updating to "${newRegion}" (Source: ${source})`);
    if (source === 'observer') {
      isSyncingRef.current = true;
      setTimeout(() => isSyncingRef.current = false, 1000);
    }
    setRegionState(newRegion);
    localStorage.setItem("api-region-preference", newRegion);
    updateAllContentOnPage(newRegion);
    if (source === 'user-click') {
      window.dispatchEvent(new CustomEvent("regionChange", {
        detail: newRegion
      }));
      attemptToUpdateNativeDropdown(newRegion, 0);
      setTimeout(() => attemptToUpdateNativeDropdown(newRegion, 1), 500);
      setTimeout(() => attemptToUpdateNativeDropdown(newRegion, 2), 1500);
    }
  };
  const attemptToUpdateNativeDropdown = (targetRegion, attempt) => {
    if (isSyncingRef.current) return;
    try {
      const targetUrl = targetRegion === "eu" ? "https://whitebit.eu" : "https://whitebit.com";
      const targetDesc = targetRegion === "eu" ? "EU Server" : "Production Server";
      const selects = document.querySelectorAll('select');
      for (const select of selects) {
        if (select.innerHTML.includes('whitebit.com') || select.innerHTML.includes('whitebit.eu')) {
          select.value = targetUrl;
          select.dispatchEvent(new Event('change', {
            bubbles: true
          }));
          return;
        }
      }
      const buttons = Array.from(document.querySelectorAll('button, [role="combobox"]'));
      const serverSelector = buttons.find(btn => {
        if (btn.closest('a') || btn.closest('[class*="card"]') || btn.closest('nav')) {
          return false;
        }
        const txt = btn.textContent || "";
        const isServerDropdown = (txt.includes('Production Server') || txt.includes('EU Server') || txt.includes('WhiteBIT Global Server') || txt.includes('WhiteBIT EU Server')) && !txt.includes('Run') && !txt.includes('Send') || btn.getAttribute('role') === 'combobox';
        return isServerDropdown;
      });
      if (serverSelector) {
        const currentText = serverSelector.textContent || "";
        if (currentText.includes(targetDesc)) return;
        serverSelector.click();
        setTimeout(() => {
          const options = document.querySelectorAll('[role="option"], li, button');
          for (const opt of options) {
            const optText = opt.textContent || "";
            if (optText.includes(targetDesc) || optText.includes(targetUrl)) {
              opt.click();
              return;
            }
          }
        }, 100);
      }
    } catch (e) {
      console.error("[Sync] Error:", e);
    }
  };
  useEffect(() => {
    if (!EU_ENABLED) return;
    setMounted(true);
    updateAllContentOnPage(region);
    const handleStorageChange = e => {
      if (e.key === "api-region-preference" && e.newValue) {
        updateRegion(e.newValue, 'storage');
      }
    };
    const handleRegionChange = e => {
      if (e.detail !== region) {
        updateRegion(e.detail, 'event');
      }
    };
    window.addEventListener("storage", handleStorageChange);
    window.addEventListener("regionChange", handleRegionChange);
    observerRef.current = new MutationObserver(mutations => {
      if (isSyncingRef.current) return;
      updateAllContentOnPage(region);
      for (const mutation of mutations) {
        if (mutation.type !== 'childList' && mutation.type !== 'characterData') continue;
        const target = mutation.target;
        const el = target.nodeType === Node.TEXT_NODE ? target.parentElement : target;
        if (el && (el.getAttribute('role') === 'option' || el.closest('[role="listbox"]'))) continue;
        const text = target.textContent || "";
        if (text.includes('WhiteBIT EU Server') || text.includes('https://whitebit.eu') && text.includes('Server')) {
          if (el && el.tagName !== 'A' && !el.closest('.region-toggle-component')) {
            if (region !== 'eu') updateRegion('eu', 'observer');
          }
        } else if (text.includes('WhiteBIT Global Server') || text.includes('https://whitebit.com') && text.includes('Server')) {
          if (el && el.tagName !== 'A' && !el.closest('.region-toggle-component')) {
            if (region !== 'com') updateRegion('com', 'observer');
          }
        }
      }
    });
    observerRef.current.observe(document.body, {
      childList: true,
      subtree: true,
      characterData: true
    });
    if (typeof window !== 'undefined') {
      const current = localStorage.getItem("api-region-preference");
      if (current) attemptToUpdateNativeDropdown(current, 'init');
    }
    return () => {
      window.removeEventListener("storage", handleStorageChange);
      window.removeEventListener("regionChange", handleRegionChange);
      if (observerRef.current) observerRef.current.disconnect();
    };
  }, [region]);
  const apiBaseUrl = protocol === "wss" ? region === "eu" ? "wss://api.whitebit.eu/ws" : "wss://api.whitebit.com/ws" : region === "eu" ? "https://whitebit.eu" : "https://whitebit.com";
  const baseUrlLabel = protocol === "wss" ? "WebSocket URL" : "Base URL";
  if (!EU_ENABLED) {
    if (!showBaseUrl) return null;
    return <div className={`flex items-center gap-2 flex-wrap my-4 region-toggle-component ${className}`}>
                <span className="text-sm text-gray-500 dark:text-gray-400 font-mono">
                    {baseUrlLabel}
                </span>
                <span className="text-sm text-gray-400">:</span>
                {protocol === "wss" ? <code className="text-sm font-mono text-primary dark:text-primary-light">
                        {apiBaseUrl}
                    </code> : <a href={apiBaseUrl} target="_blank" rel="noopener noreferrer" className="text-sm font-mono text-primary dark:text-primary-light hover:underline">
                        {apiBaseUrl}
                    </a>}
            </div>;
  }
  if (!mounted) return null;
  return <div className={`flex items-center gap-2 flex-wrap my-4 region-toggle-component ${className}`}>
            <span className="text-sm text-gray-500 dark:text-gray-400 font-mono">
                {baseUrlLabel}
            </span>
            <span className="text-sm text-gray-400">(</span>
            <div className="inline-flex bg-gray-100 dark:bg-gray-800 rounded-lg p-0.5 border border-gray-200 dark:border-gray-700">
                <button onClick={() => updateRegion("com", "user-click")} className={`px-2 py-0.5 text-xs font-medium rounded-md transition-all ${region === "com" ? "bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm" : "text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"}`}>
                    .com
                </button>
                <button onClick={() => updateRegion("eu", "user-click")} className={`px-2 py-0.5 text-xs font-medium rounded-md transition-all ${region === "eu" ? "bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm" : "text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"}`}>
                    .eu
                </button>
            </div>
            <span className="text-sm text-gray-400">)</span>
            {showBaseUrl && <>
                    <span className="text-sm text-gray-400">:</span>
                    {protocol === "wss" ? <code className="text-sm font-mono text-primary dark:text-primary-light">
                            {apiBaseUrl}
                        </code> : <a href={apiBaseUrl} target="_blank" rel="noopener noreferrer" className="text-sm font-mono text-primary dark:text-primary-light hover:underline">
                            {apiBaseUrl}
                        </a>}
                </>}
        </div>;
};

<Note>
  **For developers.** This page covers the technical integration. For what YaaS is, the model, and how
  to apply, see the [Yield-as-a-Service overview](/guides/yaas-overview).
</Note>

<RegionBaseUrl />

Yield-as-a-Service uses **Model A** — one [sub-account](/products/sub-accounts/overview) per end
user, with the [Crypto Lending](/products/lending/overview) lifecycle run inside each sub-account
using that sub-account's own API key. This guide covers the B2B layer — provisioning, funding, and
per-user reconciliation. The lending lifecycle itself is documented in the
[Crypto Lending quickstart](/products/lending/quickstart); the only difference here is the signing
key.

## Prerequisites

* **Crypto Lending enabled where it runs.** Lending is available on the master account with B2B access; on sub-accounts it is **not enabled by default** — request enablement for each end-user sub-account via the account manager or `institutional@whitebit.com` (see the [overview](/guides/yaas-overview#how-to-get-started)). Until it is enabled, the lending endpoints are not available on that sub-account.
* **A sub-account per end user**, each with its own **type:2** API key (info, trading, deposits, withdraws) — type:2 is required to run lending on a sub-account.
* **Funds in the master Main balance** to allocate to each sub-account.
* **HMAC-signed requests** — see [Authentication](/api-reference/authentication).

## Integration building blocks

### 1. Provision a sub-account per end user

Create one sub-account per end user and issue that sub-account a type:2 API key. Build with
[Sub-Accounts](/products/sub-accounts/overview) for account creation and key management, and the
[Embedded Trading integration guide](/guides/sub-account-integration) for the full walkthrough
(per-customer keys, KYC-URL branding).

| Endpoint                                  | Purpose                                                          |
| ----------------------------------------- | ---------------------------------------------------------------- |
| `POST /api/v4/sub-account/create`         | Create a sub-account (`alias`, `permissions`)                    |
| `POST /api/v4/sub-account/api-key/create` | Issue the sub-account a key (`type: 2`, `subAccountId`, `title`) |

### 2. Fund the sub-account

Move funds from the master Main balance to the end user's sub-account with a fee-free transfer.

| Endpoint                            | Purpose                                                          |
| ----------------------------------- | ---------------------------------------------------------------- |
| `POST /api/v4/sub-account/transfer` | `id` (sub-account), `direction: main_to_sub`, `ticker`, `amount` |

### 3. Offer yield inside the sub-account

Run the Crypto Lending lifecycle — fetch plans, invest, track, withdraw — signed with the
**sub-account's own key**, so investments and interest stay in that sub-account's balance. The
request and response fields are documented in the
[Crypto Lending quickstart](/products/lending/quickstart); the only B2B difference is the signing key.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
# Invest on an end user's behalf — signed with THAT sub-account's type:2 key
curl -X POST https://whitebit.com/api/v4/main-account/smart-flex/investments/invest \
  -H "Content-Type: application/json" \
  -H "X-TXC-APIKEY: SUBACCOUNT_API_KEY" \
  -H "X-TXC-PAYLOAD: PAYLOAD" \
  -H "X-TXC-SIGNATURE: SIGNATURE" \
  -d '{
    "plan": "PLAN_ID",
    "amount": "100.00",
    "withReinvest": true,
    "request": "/api/v4/main-account/smart-flex/investments/invest",
    "nonce": 1709340000000
  }'
```

<Note>
  The `main-account/` path segment addresses the **signing account's own Main balance** — for a
  sub-account key, that is the sub-account's balance, not the master's. This is why a sub-account key
  runs the lending lifecycle against that end user's funds.
</Note>

| Endpoint                                                    | Purpose                                  |
| ----------------------------------------------------------- | ---------------------------------------- |
| `POST /api/v4/main-account/smart-flex/plans`                | Available flexible plans                 |
| `POST /api/v4/main-account/smart-flex/investments/invest`   | Invest on the end user's behalf          |
| `POST /api/v4/main-account/smart-flex/investments/withdraw` | Withdraw to the sub-account Main balance |
| `POST /api/v4/main-account/smart/plans`                     | Available fixed plans                    |

### 4. Reconcile earnings per user

Each sub-account's investments and payment history are that end user's earnings. There is no
aggregated cross-sub report, so attribution is per sub-account — read each sub-account with its own key.

| Endpoint                                                           | Purpose                                    |
| ------------------------------------------------------------------ | ------------------------------------------ |
| `POST /api/v4/main-account/smart-flex/investments`                 | The end user's active flexible investments |
| `POST /api/v4/main-account/smart-flex/investments/payment-history` | Interest credited to the end user          |

Per-user lifecycle:

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
sequenceDiagram
    autonumber
    participant P as Partner backend
    participant M as Master account
    participant S as End-user sub-account
    participant L as Crypto Lending

    P->>S: Create sub-account + type:2 key
    P->>M: POST /sub-account/transfer (main_to_sub)
    M-->>S: Balance allocated
    P->>L: Invest, signed with the sub-account key
    L-->>S: Interest credited to the sub-account balance
    P->>L: Withdraw to the sub-account Main balance
    P->>P: Reconcile per sub-account (payment-history)
```

## Testing

WhiteBIT does not offer a public testnet or sandbox. Test on the live API with minimum amounts, and
check available plans and per-plan minimums via the plans endpoints first.

## Reference

* [Sub-Accounts](/products/sub-accounts/overview) — create and manage per-user accounts and keys.
* [Crypto Lending](/products/lending/overview) — Fixed and Flex plan mechanics, supported assets, and the full endpoint reference.
* [Crypto Lending quickstart](/products/lending/quickstart) — the invest, track, and withdraw lifecycle.

## What's next

<CardGroup cols={3}>
  <Card title="FAQ" icon="circle-question" href="/guides/yaas-faq">
    How B2B yield differs from the self-service flow.
  </Card>

  <Card title="Crypto Lending" icon="piggy-bank" href="/products/lending/overview">
    Plan mechanics, supported assets, and the endpoint reference.
  </Card>

  <Card title="Sub-Accounts" icon="sitemap" href="/products/sub-accounts/overview">
    Create and manage the per-user accounts this program provisions.
  </Card>
</CardGroup>
