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

# Connect a Wix app

> Import your Wix App Market payouts, then optionally send customer details and confirmed cancellations from your app's webhooks.

Wix doesn't give other apps access to your App Market billing, so OneMRR builds your Wix MRR from the payout export in your Wix developer dashboard. Upload it once for your history, then again each month.

You can also forward your app's Wix webhooks to OneMRR. They add each site's name and email, and they tell OneMRR when a site turns off auto-renewal, so the cancellation is confirmed rather than inferred from a missed renewal.

## 1. Add the Wix source

On **Connections**, select **Connect a source**, then **Wix**, then **Add Wix**. The app fields are optional. See [Connect a billing platform](https://docs.onemrr.dev/get-started/connect.md) for the other platforms.

## 2. Download your payouts from Wix

**1. Open Payouts**

In your Wix developer dashboard, open your app, then **Manage → App Market → Payouts**.

![The Wix app dashboard with Manage, App Market, Payouts outlined in the sidebar](https://mintcdn.com/one-mrr/a-KSraMTdGEGEg_T/images/get-started/wix-payouts-nav.png?fit=max\&auto=format\&n=a-KSraMTdGEGEg_T\&q=85\&s=c628552f4d9b1f11e7e8c89fa8d041f9)

**2. Select Download Data**

![The Wix Payouts overview with the Download Data button outlined](https://mintcdn.com/one-mrr/a-KSraMTdGEGEg_T/images/get-started/wix-download-data.png?fit=max\&auto=format\&n=a-KSraMTdGEGEg_T\&q=85\&s=ab51b0ed4a7a6e2008869bffbf18654e)

**3. Choose the months and download**

For your first upload, choose **From** the first month your app charged anyone, and **To** this month. After that, choose from the month of your last export to this month. Overlapping months are fine: OneMRR counts each payment once.

![The Download data dialog with From and To months, and the Download button outlined](https://mintcdn.com/one-mrr/a-KSraMTdGEGEg_T/images/get-started/wix-date-range.png?fit=max\&auto=format\&n=a-KSraMTdGEGEg_T\&q=85\&s=e39992de1f3ead9808c47107cf79b8a8)

**4. Wait for the CSV**

Wix generates the file, which can take a few minutes. You can close the dialog: Wix shows a notification when the file is ready to download.

![The Download data dialog saying Generating CSV. This may take a few minutes.](https://mintcdn.com/one-mrr/a-KSraMTdGEGEg_T/images/get-started/wix-generating.png?fit=max\&auto=format\&n=a-KSraMTdGEGEg_T\&q=85\&s=2de06837a421e9b1f81d7cc6b6fe8874)

## 3. Upload it to OneMRR

On **Connections**, select the Wix app, then **Upload payout CSV**, and choose the file. OneMRR rebuilds your Wix history from every file you've uploaded, so keep uploading new exports rather than replacing old ones. The source shows how far your payouts reach, such as **Payouts through Oct 31**.

![The Wix source on Connections with Upload payout CSV outlined](https://mintcdn.com/one-mrr/r0eSCeyIYQOzU0L6/images/get-started/wix-upload.png?fit=max\&auto=format\&n=r0eSCeyIYQOzU0L6\&q=85\&s=7ceb5a9d6a1c055a60886112d0ab05b7)

How the export becomes MRR:

| In the export | In OneMRR |
| - | - |
| A site's first charge, and each renewal | MRR for that site: the monthly price, or a yearly price ÷ 12 |
| A plan switch | A plan change, at the next full price |
| No renewal by the end of the paid term | Churn at the end of the term, after a 10-day grace period (30 days for yearly plans) |
| A refund or chargeback of the latest payment | Churn on that date |
| One-time purchases | Collections only, never MRR |
| Wix's revenue share | Marketplace fees, kept apart from MRR |

Wix converts other currencies to USD at each payment's rate. Small changes from that are recorded as currency movements, not expansion or contraction. See [How MRR is calculated](https://docs.onemrr.dev/analytics/how-mrr-is-calculated.md).

> [!NOTE]
>
> Between uploads, OneMRR only knows the payments in your last export. A site that renewed after it can look lapsed once its term and grace period pass, so upload at least once a month.

## 4. Optional: send your Wix webhooks to OneMRR

The payout export has no emails, and it only shows a cancellation once a renewal is missed. If your app has a server that receives Wix webhooks, send OneMRR:

* **Customer details:** each site's name and owner email, so Wix customers have names and emails in OneMRR.
* **Confirmed cancellations:** when a site turns off auto-renewal, it becomes a scheduled cancellation on [Ending soon](https://docs.onemrr.dev/churn/ending-soon.md), counted as confirmed rather than inferred. It shows once your uploaded payouts reach the site's current term.

Webhooks never change MRR. Payments still come from your payout exports.

### Create an API key

Go to **Settings → API**, enter a name under **New key**, such as Wix webhooks, and select **Create key**. Copy the key straight away: it's shown only once. Keep it on your server, never in your app's frontend.

![Settings, API, with the New key form and Create key outlined](https://mintcdn.com/one-mrr/a-KSraMTdGEGEg_T/images/workspace/api-key.png?fit=max\&auto=format\&n=a-KSraMTdGEGEg_T\&q=85\&s=a9224d1c968ef7a7829af07864228769)

### Find the Wix source ID

On **Connections**, open the Wix app, then **Setup**. Copy the **API source ID**. Every request sends it as `source_id`.

![The Wix source's Setup section with API source ID outlined](https://mintcdn.com/one-mrr/a-KSraMTdGEGEg_T/images/get-started/wix-source-id.png?fit=max\&auto=format\&n=a-KSraMTdGEGEg_T\&q=85\&s=52dc6dcce67877571fbb9af543befadc)

Store both on your server:

```bash .env theme={null}
ONEMRR_API_KEY=sk_live_...
ONEMRR_WIX_SOURCE_ID=123
```

### Subscribe to the webhooks

In your Wix app's dashboard, subscribe your webhook URL to these App Instance events:

| Wix event | What to send OneMRR |
| - | - |
| App Instance Installed | The customer's name and email |
| Paid Plan Purchased | The customer's name and email |
| Paid Plan Changed | The customer's name and email |
| Plan Converted to Paid | The customer's name and email |
| Paid Plan Auto Renewal Cancelled | A scheduled cancellation |
| Plan Reactivated | That the cancellation was withdrawn |

You don't need **App Instance Removed**: the payouts show when payments stop.

### Call OneMRR from your server

A small helper for OneMRR's API. Customers are keyed by the site's Wix **instance ID**, the same ID as the **Instance ID** column in the payout export, so OneMRR matches them up.

```js onemrr.js theme={null}
const ONEMRR_API = "https://app.onemrr.dev/api/v1";
const SOURCE_ID = Number(process.env.ONEMRR_WIX_SOURCE_ID);

export async function onemrr(path, body) {
  const res = await fetch(`${ONEMRR_API}${path}`, {
    method: "POST",
    headers: {
      authorization: `Bearer ${process.env.ONEMRR_API_KEY}`,
      "content-type": "application/json",
    },
    body: JSON.stringify({ source_id: SOURCE_ID, ...body }),
  });
  if (!res.ok) {
    const error = await res.json().catch(() => null);
    throw new Error(`OneMRR ${res.status}: ${error?.error?.message ?? "request failed"}`);
  }
  return res.json();
}
```

Requests are safe to repeat. A customer is updated in place, and a subscription event with the same `external_id` is recorded once, so a webhook Wix delivers twice is still counted once.

### Receive Wix webhooks

Wix signs each webhook as a JWT. Verify it with your app's public key, from the **Webhooks** page of your app's dashboard, then pass the event to its handler. Answer with an error when a handler fails, so Wix delivers the webhook again.

```js server.js theme={null}
import express from "express";
import jwt from "jsonwebtoken";
import { handlers } from "./wix-handlers.js";

const app = express();

app.post("/webhooks/wix", express.text({ type: "*/*" }), async (req, res) => {
  let event;
  try {
    const { data } = jwt.verify(req.body, process.env.WIX_PUBLIC_KEY, { algorithms: ["RS256"] });
    event = JSON.parse(data);
  } catch {
    return res.status(401).send("Invalid Wix webhook");
  }

  const { instanceId, eventType } = event;
  const content = JSON.parse(event.data);
  try {
    await handlers[eventType]?.(instanceId, content);
    res.send("ok");
  } catch (err) {
    console.error(`Wix ${eventType} for ${instanceId}:`, err);
    res.status(503).send("Retry");
  }
});

app.listen(3000);
```

> [!TIP]
>
> If OneMRR is unreachable, the handler fails and Wix retries for a while. To never lose an event, save each webhook first and send it to OneMRR from a queue that retries.

### Handle each event

```js wix-handlers.js theme={null}
import { onemrr } from "./onemrr.js";
import { findSite } from "./db.js"; // Your own lookup: the site's name and owner email.

// The site's name and owner email. Wix webhooks carry neither, so look them up.
async function upsertCustomer(instanceId) {
  const site = await findSite(instanceId);
  await onemrr("/customers", {
    external_id: instanceId,
    name: site?.name,
    email: site?.ownerEmail,
  });
}

export const handlers = {
  AppInstalled: (instanceId) => upsertCustomer(instanceId),
  PaidPlanPurchased: (instanceId) => upsertCustomer(instanceId),
  PaidPlanChanged: (instanceId) => upsertCustomer(instanceId),
  PlanConvertedToPaid: (instanceId) => upsertCustomer(instanceId),

  // The site turned off auto-renewal. It keeps the plan until the end of the paid term.
  async PaidPlanAutoRenewalCancelled(instanceId, content) {
    // A cancelled free trial was never paid, so there's no MRR to end.
    if (content.cancelledDuringFreeTrial === "DURING_FREE_TRIAL") return;
    await onemrr("/subscription-events", {
      external_id: `wix_cancel_${instanceId}_${content.operationTimeStamp}`,
      customer_external_id: instanceId,
      subscription_external_id: `wix_sub_${instanceId}`,
      type: "cancellation_scheduled",
      occurred_at: content.operationTimeStamp,
      // No paid_through: OneMRR takes the end of the term from your payouts.
    });
  },

  // The site turned auto-renewal back on.
  async PlanReactivated(instanceId, content) {
    await onemrr("/subscription-events", {
      external_id: `wix_reactivated_${instanceId}_${content.operationTimeStamp}`,
      customer_external_id: instanceId,
      subscription_external_id: `wix_sub_${instanceId}`,
      type: "cancellation_retracted",
      occurred_at: content.operationTimeStamp,
    });
  },
};
```

#### When the cancellation ends

Wix's webhook doesn't say when the paid term ends, and Wix's **Get App Instance** only returns `billing.expirationDate` for yearly and longer plans. So leave `paid_through` out: OneMRR takes the end of the term the site last paid for from your payouts. If a payment after the cancellation shows the site renewed after all, OneMRR ignores the cancellation.

If you do know the end date, send it as `paid_through`, and OneMRR uses it as is:

```js theme={null}
paid_through: instance.billing.expirationDate, // From Wix's Get App Instance, for yearly plans
```

> [!NOTE]
>
> A cancellation sent before your latest upload covers the site's current term waits for that upload. Until then, the site stays active in OneMRR.

### Check it worked

The handler's calls answer with a status:

* `/customers`: `created`, `updated` or `unchanged`. The name and email show on the customer in OneMRR straight away.
* `/subscription-events`: `created` the first time, `unchanged` when Wix delivers the same webhook again. Each new event queues a Wix rebuild, and the cancellation then shows under [Ending soon](https://docs.onemrr.dev/churn/ending-soon.md).

The full API reference is at [app.onemrr.dev/api/v1/openapi.json](https://app.onemrr.dev/api/v1/openapi.json).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.