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

# Self-Hosting

> Run the Appeeky Analytics server in your own Cloudflare account

The Appeeky Analytics server is source-available. You can run it in your own Cloudflare account on Workers and D1, and point the SDK at it. Your data then stays in your account.

The self-hosted server runs the **same API** as the hosted service, and the hosted service gives each Appeeky account a database with the same schema, so both behave the same way. The paths differ: on your server, the dashboard views live under `/v1/apps/:id/...` and use your admin token instead of an Appeeky API key.

> [!NOTE]
>
> Self-hosting covers the analytics server and its own dashboard. Features that live in Appeeky, such as ROAS with Apple Search Ads spend, the Appeeky Alerts bot and the reuse of your Appeeky RevenueCat webhook, are only part of the hosted service.

***

## Requirements

* Node 20 or later
* A Cloudflare account. The Workers free plan is enough to start.

***

## Deploy

**1. Create the database**

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
git clone <repository> && cd appeeky-analytics
npm install
cd apps/worker
npx wrangler login
npx wrangler d1 create appeeky-analytics
```

Copy the `database_id` that `d1 create` prints into `apps/worker/wrangler.toml`.

**2. Set secrets and deploy**

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
# Protects the management API. Use a long random value.
openssl rand -hex 32 | npx wrangler secret put ADMIN_TOKEN
# Paste the same value into RevenueCat as the webhook Authorization header.
openssl rand -hex 32 | npx wrangler secret put REVENUECAT_WEBHOOK_SECRET
npm run deploy   # applies D1 migrations, then deploys the worker
```

The deploy prints your worker URL, for example `https://appeeky-analytics.<you>.workers.dev`. You can put it on your own domain under Workers → Settings → Domains & Routes.

**3. Open the dashboard**

Open `https://<worker>/` and sign in with your `ADMIN_TOKEN`. The token is kept in that browser's local storage until you sign out.

**4. Add an app**

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://<worker>/v1/apps \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "My App", "platform": "apple"}'
```

The response has the app's `publishableKey` (`apk_pub_…`). Use `"platform": "google"` for a Play Store app.

**5. Point the SDK at your server**

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
Appeeky.init({ key: "apk_pub_...", endpoint: "https://<worker>/v1/ingest" });
```

**6. Connect RevenueCat**

In RevenueCat, open Integrations → Webhooks and add a webhook:

* URL: `https://<worker>/v1/webhooks/revenuecat`
* Authorization header: your `REVENUECAT_WEBHOOK_SECRET`
* Environment: production

In the app, call `Appeeky.identify(await Purchases.getAppUserID())`.

***

## App Settings

Change an app with `PATCH /v1/apps/:id` and your admin token:

| Field | Description |
| - | - |
| `name` | App name |
| `notifySales` | Telegram sale messages on or off |
| `timeZone` | IANA time zone name. Days and hours default to UTC. |
| `funnel` | 2 to 6 funnel steps, or `null` for the default |
| `storeAppId` | Apple ID or Play package. Matches SKAdNetwork postbacks to the app. |

`POST /v1/apps/:id/rotate-key` issues a new publishable key, and `DELETE /v1/apps/:id` deletes the app and all of its data.

***

## Telegram Sale Messages

1. Create a bot with [@BotFather](https://t.me/BotFather) and copy its token.
2. Send the bot a message, or add it to a group or channel.
3. Find the chat id: open `https://api.telegram.org/bot<token>/getUpdates` and read `message.chat.id`.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
npx wrangler secret put TELEGRAM_BOT_TOKEN
npx wrangler secret put TELEGRAM_CHAT_ID
curl -X POST https://<worker>/v1/telegram/test -H "Authorization: Bearer $ADMIN_TOKEN"
```

***

## SKAdNetwork Postback Copies

iOS sends postback copies to the registrable domain named in the app's Info.plist, at `/.well-known/skadnetwork/report-attribution/` and `/.well-known/appattribution/report-attribution/`.

1. Set the config plugin's endpoint to your domain: `["@appeeky/analytics-react-native", { "attributionEndpoint": "https://example.com" }]`.
2. Serve those two paths from the worker on that domain, with a Workers route or a rewrite on your site.
3. Set the app's `storeAppId` to its Apple ID.

***

## Limits

Ingest is limited to 600 batches per minute per publishable key (`[[ratelimits]]` in `wrangler.toml`). The SDK keeps the batch and retries when it gets `429`.

***

## License

The server is released under FSL-1.1-MIT. You can use, modify and self-host it for any purpose except offering a competing commercial product. Each version becomes MIT two years after its release.

***

## Related Docs

* [Analytics API](https://docs.appeeky.com/docs/analytics-api.md)
* [SDK Reference](https://docs.appeeky.com/docs/analytics-sdk-reference.md)
* [Overview](https://docs.appeeky.com/docs/analytics-overview.md)


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