# Embeddable Components Guide

## Overview

Embeddable components enable Forge dashboard functionality to be embedded within third-party websites or applications. Integrators can provide
users with direct access to billing management, carrier setup, payment logs, and reporting, all from their own platform.

---

## Prerequisites

- A Forge-enabled EasyPost account
- A server endpoint capable of fetching a session ID from the EasyPost EmbeddableSessions API

---

## Server Configuration

The server must expose an endpoint that retrieves a short-lived session ID from the EasyPost EmbeddableSessions API. This endpoint is
called by the client application (browser) to initialize an embedded component.

The server-side request to EasyPost should be made with the platform’s EasyPost API key and include the following parameters:

- `user_id`: The User ID of the sub account for which the embeddable session is being created.
- `origin_host` The integrator’s domain in bare-host format (e.g., example.com), excluding protocol and subdomains.

#### Retrieve a Session ID Example (Python)

```python
import os
import requests
from flask import (
    Flask,
    jsonify,
)

app = Flask(__name__)

EASYPOST_API_KEY = os.environ.get("EASYPOST_API_KEY", "")

@app.route("/api/easypost-embeddables/session", methods=["GET"])
def create_embeddable_session():
    payload = {
        "user_id": "SUB_ACCOUNT_USER_ID",  # Replace with sub-account user ID
        "origin_host": "ORIGIN_HOST",  # Replace with integrator's domain
    }

    response = requests.post(
        "https://api.easypost.com/v2/embeddables/session",
        auth=(EASYPOST_API_KEY, ""),
        json=payload,
        headers={"Content-Type": "application/json"},
        timeout=10,
    )
    response.raise_for_status()
    return jsonify(response.json())

if __name__ == "__main__":
    app.run(port=5000, debug=True)
```

---

## Client Configuration

The client application must load the EasyPost Embeddables script and initialize the SDK with a function that fetches a session ID from the server.

Each embeddable component is rendered securely in an EasyPost-hosted iframe displayed inside a modal. Components are launched asynchronously in
response to user actions such as button clicks.

#### Example Integration that Corresponds to the Above Python Snippet (HTML/JavaScript)

```html
<html>
  <body>
    <div>Integrator's Website</div>
    <div id="container"></div>
    <div>
      <button id="button-billing">Manage Billing</button>
      <button id="button-carriers">Manage Carriers</button>
      <button id="button-payment-logs">Payment Logs</button>
      <button id="button-reports">Reports</button>
    </div>

    <!-- Load EasyPost Embeddables -->
    <script src="https://embed.easypost.com/embeddables/1.0/client.js" async></script>

    <script>
      window.EasyPostEmbeddables = window.EasyPostEmbeddables || {};
      window.EasyPostEmbeddables.onLoad = () => {
        // Function that calls the integrator's backend to get a session ID
        const fetchSessionId = async () => {
          const url = '/api/easypost-embeddables/session'; // your server endpoint
          try {
            const response = await fetch(url);

            if (!response.ok) {
              throw new Error(`Fetch Session ID error! status: ${response.status}`);
            }

            const data = await response.json();
            return data.session_id;
          } catch (error) {
            console.error('[EasyPostEmbeddables] Failed to fetch session ID:', error);
            return null;
          }
        };

        // Initialize EasyPost Embeddables
        const embeddables = window.EasyPostEmbeddables.init({
          fetchSessionId,
          fonts: [
            {
              cssSrc: 'https://fonts.googleapis.com/css2?family=Zen+Dots&display=swap',
            },
          ],
          appearance: {
            tokens: {
              'font.family': 'Zen Dots',
            },
            modalZIndex: '123456',
          },
        });

        // Example UI bindings
        document.getElementById('button-billing').addEventListener('click', async () => {
          await embeddables.open('manage-billing');
        });

        document.getElementById('button-carriers').addEventListener('click', async () => {
          await embeddables.open('manage-carriers');
        });

        document.getElementById('button-payment-logs').addEventListener('click', async () => {
          await embeddables.open('manage-payment-logs');
        });

        document.getElementById('button-reports').addEventListener('click', async () => {
          await embeddables.open('manage-reports');
        });
      };
    </script>
  </body>
</html>
```

---

## Embeddable Component Workflow

1. The browser loads the EasyPost Embeddables script from  https://embed.easypost.com/embeddables/1.0/client.js.
2. When `onLoad` fires, the integrator’s client initializes the SDK using `EasyPostEmbeddables.init()`.
3. The `fetchSessionId()` callback retrieves or refreshes the session ID as needed.
4. When a button is clicked, the `open()` method launches the requested component inside a modal.
5. The modal content is rendered securely from EasyPost’s domain.

> Note: Session refreshing is handled automatically by EasyPost. Only the `fetchSessionId` function must be
  supplied.

---

## Initialization Options

[Content omitted: table content. Review the rendered documentation for complete tabular data.]

### Fonts

The `fonts` option in `EasyPostEmbeddables.init()` accepts an array of font configuration objects that define how custom fonts are loaded inside
embeddable components. If the application uses custom fonts, they must be specified when initializing the SDK.

Custom fonts can be specified using one of two forms:

1. `cssSrc`: Use this form to load a font from a hosted CSS file (for example, a Google Fonts stylesheet). The URL must point to a valid CSS file defining `@font-face` rules.

```javascript
EasyPostEmbeddables.init({
  fonts: [{ cssSrc: 'https://fonts.googleapis.com/css2?family=Zen+Dots&display=swap' }],
});
```

2. **Direct Font Files:** Use this form to load a font file directly. Note that in this form, `family` and`src` are required.

```javascript
EasyPostEmbeddables.init({
  fonts: [
    {
      family: 'Avenir',
      src: 'https://example.com/fonts/avenir.woff2',
      style: 'normal',
      weight: '400',
    },
  ],
});
```

### Appearance

The `appearance` option in `EasyPostEmbeddables.init()` controls the visual presentation of Embeddable components. It supports customization of
theme tokens (color and typography) and modal layout properties such as stacking order. Integrators can pass either theme tokens to modify
colors and font settings, or define a `modalZIndex` value to control how the modal overlays the application interface.

#### Theme tokens

The tokens are organized by categories that represent different parts of the visual system:

- **Primary:** Main brand palette for key actions and highlights. The default is a blue shade.
- **Secondary:** Accent tones used for emphasis or alternate states. The default is a purple shade.
- **Positive:** Success, confirmation, or active states. The default is a green shade.
- **Negative:** Errors or destructive actions. The default is a red shade.
- **Alert:** Urgent notifications or attention states. The default is an orange shade.
- **Warning:** Caution or mild alerts. The default is a yellow shade.
- **Neutral:** Grayscale foundation for surfaces, borders, and text.

Font family defines the default typeface used throughout Embeddable components. By default, components use `Poppins`, with `“Poppins Fallback”` for compatibility.

```json
{
  "color.primary.900": { "value": "#000824" },
  "color.primary.800": { "value": "#061340" },
  "color.primary.700": { "value": "#0B2780" },
  "color.primary.600": { "value": "#113ABF" },
  "color.primary.500": { "value": "#164DFF" },
  "color.primary.400": { "value": "#4571FF" },
  "color.primary.300": { "value": "#7394FF" },
  "color.primary.200": { "value": "#A2B8FF" },
  "color.primary.100": { "value": "#D0DBFF" },
  "color.primary.050": { "value": "#F6F9FF" },
  "color.primary.025": { "value": "#FBFDFF" },

  "color.secondary.900": { "value": "#150720" },
  "color.secondary.800": { "value": "#280E3C" },
  "color.secondary.700": { "value": "#501D77" },
  "color.secondary.600": { "value": "#772BB0" },
  "color.secondary.500": { "value": "#9F39EE" },
  "color.secondary.400": { "value": "#B261F1" },
  "color.secondary.300": { "value": "#C588F5" },
  "color.secondary.200": { "value": "#D9B0F8" },
  "color.secondary.100": { "value": "#ECD7FC" },
  "color.secondary.050": { "value": "#F9F1FF" },
  "color.secondary.025": { "value": "#FDFBFF" },

  "color.positive.900": { "value": "#001E16" },
  "color.positive.800": { "value": "#00392C" },
  "color.positive.700": { "value": "#007357" },
  "color.positive.600": { "value": "#00AC83" },
  "color.positive.500": { "value": "#00E5AE" },
  "color.positive.400": { "value": "#33EABE" },
  "color.positive.300": { "value": "#66EFCE" },
  "color.positive.200": { "value": "#99F5DF" },
  "color.positive.100": { "value": "#CCFAEF" },
  "color.positive.050": { "value": "#EAFFFA" },
  "color.positive.025": { "value": "#FAFFFE" },

  "color.negative.900": { "value": "#1B0707" },
  "color.negative.800": { "value": "#3F1716" },
  "color.negative.700": { "value": "#7E2E2D" },
  "color.negative.600": { "value": "#BC4543" },
  "color.negative.500": { "value": "#FB5C59" },
  "color.negative.400": { "value": "#FC7D7A" },
  "color.negative.300": { "value": "#FD9D9B" },
  "color.negative.200": { "value": "#FDBEBD" },
  "color.negative.100": { "value": "#FEDEDE" },
  "color.negative.050": { "value": "#FFF2F2" },
  "color.negative.025": { "value": "#FFFCFC" },

  "color.alert.900": { "value": "#411E00" },
  "color.alert.800": { "value": "#834003" },
  "color.alert.700": { "value": "#C36108" },
  "color.alert.600": { "value": "#F2780A" },
  "color.alert.500": { "value": "#FF8D25" },
  "color.alert.400": { "value": "#FFAD61" },
  "color.alert.300": { "value": "#FFBC7D" },
  "color.alert.200": { "value": "#FFD0A4" },
  "color.alert.100": { "value": "#FFEDE0" },
  "color.alert.050": { "value": "#FFF9F5" },
  "color.alert.025": { "value": "#FFFDFD" },

  "color.warning.900": { "value": "#423804" },
  "color.warning.800": { "value": "#6C4A07" },
  "color.warning.700": { "value": "#A57B0F" },
  "color.warning.600": { "value": "#D6A015" },
  "color.warning.500": { "value": "#F4D35E" },
  "color.warning.400": { "value": "#FEE281" },
  "color.warning.300": { "value": "#FFEBA4" },
  "color.warning.200": { "value": "#FFF2C4" },
  "color.warning.100": { "value": "#FFF8DF" },
  "color.warning.050": { "value": "#FFFCF0" },
  "color.warning.025": { "value": "#FFFEFB" },

  "color.neutral.900": { "value": "#000000" },
  "color.neutral.800": { "value": "#1C222D" },
  "color.neutral.700": { "value": "#384359" },
  "color.neutral.600": { "value": "#47547F" },
  "color.neutral.500": { "value": "#7086B2" },
  "color.neutral.400": { "value": "#8D9EC1" },
  "color.neutral.300": { "value": "#A9B6D1" },
  "color.neutral.200": { "value": "#C6CFE0" },
  "color.neutral.100": { "value": "#E2E7F0" },
  "color.neutral.050": { "value": "#F1F1F1" },
  "color.neutral.025": { "value": "#FAFAFA" },
  "color.neutral.000": { "value": "#FFFFFF" },

  "font.family": { "value": "Poppins, \"Poppins Fallback\"" }
}
```

---

## Embeddables Instance Methods

When `EasyPostEmbeddables.init()` is called, it returns an Embeddables instance that provides several methods for interacting with embedded components.
These methods allow integrators to open modals, listen for events, update appearance dynamically, or clean up the iframe when it’s no longer needed.

[Content omitted: table content. Review the rendered documentation for complete tabular data.]

---

## Available Components

Each component mirrors its Forge counterpart and launches inside a modal. For instance, the carrier management component allows sub-accounts to connect,
configure, and manage their carrier accounts.

### Billing Management

The Billing Management component is accessed by passing `"manage-billing"` to the `open()` function on the created Embeddables instance.

![Example of the Billing Management component](https://docs.easypost.com/images/guides/embeddables/billing-management.png)

### Carrier Management

The Carrier Management component is accessed by passing `"manage-carriers"` to the `open()` function on the created Embeddables instance.

![Example of the Carrier Management component](https://docs.easypost.com/images/guides/embeddables/carrier-management.png)

### Payment Logs

The Payment Logs component is accessed by passing `"manage-payment-logs"` to the `open()` function on the created Embeddables instance.

![Example of the Payment Logs component](https://docs.easypost.com/images/guides/embeddables/payment-logs.png)

### Reports

The Reports component is accessed by passing `"manage-reports"` to the `open()` function on the created Embeddables instance.

![Example of the Reports component](https://docs.easypost.com/images/guides/embeddables/reports.png)

---

## Content Security Policy (CSP) Considerations

Integrators using Embeddable Components should ensure the CSP allows the EasyPost client script and embedded iframe to load correctly.

Below is a minimal CSP configuration example that allows Embeddable Components to function correctly in production environments:

```bash
default-src 'self';
script-src 'self' https://embed.easypost.com 'unsafe-inline';
frame-src https://embed.easypost.com;
style-src 'self' https://assets.embed.easypost.com 'unsafe-inline' https:;
font-src https://assets.embed.easypost.com data: https:;
img-src https://assets.embed.easypost.com data:;
connect-src 'self' https://assets.embed.easypost.com;
```

---

## Additional Resources

Please visit the Forge Help Center for more information.