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

# Converge

> Connect Converge so Replo can read your attribution data in chat, and add the Converge pixel to your site.

export const TryPromptButton = ({prompt, imageSrc, imageAlt = "Template preview", imageStyles = {}, buttonCta = "Build in Replo"}) => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const PUBLISHER_API_BASE_URL = "https://publisher.replo.app";
  const APP_URL = "https://dashboard.replo.app";
  const LoaderIcon = <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
    animation: "spin 1s linear infinite"
  }}>
      <path d="M21 12a9 9 0 1 1-6.219-8.56" />
    </svg>;
  const ChevronIcon = <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="m9 18 6-6-6-6" />
    </svg>;
  async function postJSON(url, body, headers = {}, timeoutMs = 120000) {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
    try {
      const response = await fetch(url, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          ...headers
        },
        body: JSON.stringify(body ?? ({})),
        signal: controller.signal
      });
      const responseText = await response.text();
      if (!response.ok) throw new Error(`API Error: ${response.status}`);
      return responseText ? JSON.parse(responseText) : {};
    } finally {
      clearTimeout(timeoutId);
    }
  }
  async function handleTryPrompt(event) {
    event.preventDefault();
    event.stopPropagation();
    setIsLoading(true);
    setError(null);
    try {
      const body = {
        prompt,
        file: null
      };
      const {seed} = await postJSON(`${PUBLISHER_API_BASE_URL}/api/v1/marketing/issue-marketing-site-jwt`, body);
      if (!seed) throw new Error("No seed returned from API");
      const url = new URL(APP_URL);
      url.searchParams.set("type", "agent");
      url.hash = `seed=${encodeURIComponent(seed)}`;
      window.open(url.toString(), "_blank");
    } catch (caughtError) {
      console.error("Failed to generate prompt:", caughtError);
      setError("Something went wrong. Please try again.");
    } finally {
      setIsLoading(false);
    }
  }
  return <div style={{
    position: "relative",
    display: "inline-block",
    width: "100%"
  }}>
      {}
      {imageSrc && <img src={imageSrc} alt={imageAlt} style={{
    width: "100%",
    height: "auto",
    display: "block",
    borderRadius: "8px",
    opacity: 0.8,
    ...imageStyles
  }} />}

      {}
      <div style={imageSrc ? {
    position: "absolute",
    top: "50%",
    left: "50%",
    transform: "translate(-50%, -50%)",
    zIndex: 10
  } : {
    display: "flex",
    justifyContent: "flex-start",
    margin: "1.25rem 0"
  }}>
        <button type="button" className="try-replo-btn" onClick={handleTryPrompt} disabled={isLoading} style={{
    backgroundColor: "#274AE2",
    color: "#ffffff",
    padding: "0.5rem 1.1rem",
    fontSize: "0.875rem",
    fontWeight: 600,
    fontFamily: "inherit",
    border: "none",
    borderRadius: "9999px",
    cursor: isLoading ? "not-allowed" : "pointer",
    lineHeight: 1.4,
    whiteSpace: "nowrap",
    display: "inline-flex",
    alignItems: "center",
    justifyContent: "center",
    WebkitFontSmoothing: "antialiased",
    boxSizing: "border-box",
    opacity: isLoading ? 0.7 : 1,
    boxShadow: "0 1px 2px rgba(15, 23, 42, 0.08)",
    transition: "background-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease"
  }}>
          <span style={{
    visibility: isLoading ? "hidden" : "visible",
    display: "inline-flex",
    alignItems: "center",
    gap: "0.375rem"
  }}>
            {buttonCta}
            {ChevronIcon}
          </span>
          {isLoading && <span style={{
    position: "absolute",
    display: "inline-flex",
    alignItems: "center",
    justifyContent: "center"
  }}>
              {LoaderIcon}
            </span>}
        </button>
      </div>

      {}
      <style>
        {`
          @keyframes spin {
            from {
              transform: rotate(0deg);
            }
            to {
              transform: rotate(360deg);
            }
          }
          .try-replo-btn:hover:not(:disabled) {
            background-color: #1f3ec0 !important;
            box-shadow: 0 4px 12px rgba(39, 74, 226, 0.35);
            transform: translateY(-1px);
          }
          .try-replo-btn:active:not(:disabled) {
            transform: translateY(0);
            box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
          }
          .try-replo-btn:focus-visible {
            outline: 2px solid #274AE2;
            outline-offset: 2px;
          }
        `}
      </style>

      {}
      {error && <div style={{
    marginTop: "12px",
    padding: "12px 16px",
    backgroundColor: "#fee",
    border: "1px solid #fcc",
    borderRadius: "6px",
    color: "#c33",
    fontSize: "14px",
    textAlign: "center"
  }} role="alert">
          {error}
        </div>}
    </div>;
};

[Converge](https://www.runconverge.com) is an ecommerce attribution platform: it follows the customer journey across your ad channels and reports spend, revenue, and ROAS by channel and campaign. Replo works with Converge in two independent ways; set up one or both.

## Let Replo read your Converge data

Connect Converge in the Integrations app and Replo can query your attribution data directly in chat. You never leave the conversation to pull a report. Once connected, ask Replo for:

* **Performance by channel or campaign.** Revenue, orders, sessions, ad spend, ROAS, CPA, clicks, and impressions over any date range, broken down by channel group, channel, campaign, ad set, or ad, using Converge's attribution models.
* **Cohorts.** Lifetime value or retention for customers acquired in a given period.
* **Your saved reports.** The data behind an insight, dashboard, or analytics view you already built in Converge. Name the report and Replo pulls it.
* **What Converge tracks.** The events, store dimensions, and channel or campaign names in your workspace, so a question like "how is my Summer Sale campaign doing" lands on the right campaign.

Point Replo at the integration with an [@-mention](/sessions-and-history#mentions) so there is no doubt which tool you mean.

```text theme={null}
What was my ROAS by channel over the last 30 days according to @Converge?
```

<TryPromptButton prompt="What was my ROAS by channel over the last 30 days according to Converge?" />

Custom queries come back with a link to the same query in Converge, so you can open it there and keep exploring.

### Connect Converge

Converge connects with a **personal API key**; there is no sign-in flow. Like other analytics tools, the Converge card offers a **Project** connection (shared with everyone on the project) and a **Personal** one (only for you). Connect either or both.

<Steps>
  <Step title="Create an API key in Converge">
    In Converge, open your account settings and go to **API keys**. Create a key
    and copy it right away: it starts with `cvg_` and Converge only shows it
    once.
  </Step>

  <Step title="Open the Integrations app">
    In Replo, open the [Integrations app](/apps/integrations) and find
    **Converge**.
  </Step>

  <Step title="Paste the key and connect">
    In the **Project** or **Personal** block, paste the key and click
    **Connect**. If your Converge account can reach more than one workspace,
    Replo lists them and asks for the **Workspace ID** to use.
  </Step>
</Steps>

You can also connect straight from chat; if you ask Replo to use Converge before it's connected, it drops the connection card into the conversation.

```text theme={null}
Connect my Converge account
```

<TryPromptButton prompt="Connect my Converge account" />

<Note>
  A Converge API key belongs to the person who created it, not to the workspace.
  If that person is removed from the Converge workspace, the key stops working
  and the Integrations app tags the connection **Reconnect needed**. Create the
  key from an account that stays on the workspace.
</Note>

## Track your site with the Converge pixel

The pixel is separate from the data connection: it runs on your published pages so Converge can attribute the traffic and orders that come through them. Ask Replo to add it and give it the **public token** of your client-side event source.

```text theme={null}
Add Converge tracking to my site with token abc123, then publish
```

<TryPromptButton prompt="Add Converge tracking to my site with token abc123, then publish" />

Replo installs the pixel as a managed script that loads through Replo's consent layer, so it stays GDPR and CCPA compliant. Page views, product views, add to cart, checkout, and purchase events fire automatically. Tracking starts on every page once you publish. See [Adding Scripts](/features/scripts) for more on site-wide pixels.

### Finding your public token

1. In Converge, go to **Data management** and open **Event sources**.
2. Select your **client-side** source and open the **HTML** install option.
3. The token is the part of the script URL before `.js` (for example, `abc123` in `https://static.runconverge.com/pixels/abc123.js`).

<Note>
  Server-side event sources have a **webhook token** instead. It is a secret
  credential and does not work as a pixel; pasting it into your site would
  publish it. If Replo asks for a different token, use the client-side source's
  public token.
</Note>

<Note>
  The two setups are independent. The pixel feeds events into Converge; the API
  key lets Replo read attribution back out. Most merchants do both.
</Note>
