# GrowPanel for Developers

> REST API reference, CLI, MCP server, and integration guides.
> Built 2026-06-02T08:56:18.299Z · source: https://growpanel.io/developers/

---

## Building Integrations
<!-- url: https://growpanel.io/developers/building-integrations -->

This guide explains how to connect any billing system to GrowPanel. Whether you have a custom-built billing platform, use a service we don't natively support, or want to build a public integration, this guide covers everything you need to know.

---

## How GrowPanel calculates metrics

Before building an integration, it helps to understand how GrowPanel turns raw billing data into subscription metrics.

### The data model

GrowPanel works with three core data types:

| Data type | Purpose |
|-----------|---------|
| **Customers** | Who is paying you |
| **Plans** | What they're paying for (pricing, billing interval) |
| **Invoices** | Records of what they actually paid |

From these three inputs, GrowPanel calculates all subscription metrics: MRR, ARR, churn, expansion, contraction, reactivation, cohort retention, and more.

### How invoices become MRR

The key insight is that **invoices drive everything**. GrowPanel doesn't track "subscriptions" as a separate concept—it reconstructs subscription state from invoice history.

When you send an invoice, GrowPanel:

1. Looks up the customer and plan
2. Determines the billing interval (monthly, annual, etc.)
3. Normalizes the amount to monthly (annual invoices ÷ 12)
4. Compares to the customer's previous MRR
5. Classifies the change as new, expansion, contraction, or reactivation

**Example:** A customer paying $1,200/year for an annual plan contributes $100/month MRR.

### MRR movements

GrowPanel automatically detects these movement types:

| Movement | What triggers it |
|----------|------------------|
| **New** | First invoice for a customer (or first after being churned) |
| **Expansion** | Invoice with higher MRR than previous period |
| **Contraction** | Invoice with lower MRR than previous period |
| **Churn** | No invoice in the expected billing period, or cancellation invoice |
| **Reactivation** | Invoice for a customer who previously churned |

You don't need to tell GrowPanel which movement type applies—it figures this out from the invoice sequence.

---

## What you need to send

To build an integration, you need to push three things to GrowPanel:

### 1. Customers

Basic information about who is paying you.

```json
{
  "id": "cust_123",
  "name": "Acme Inc",
  "email": "billing@acme.com",
  "country": "US",
  "created": "2024-01-15T00:00:00Z"
}
```

**Required fields:**
- `id` - Your unique identifier for this customer
- `name` - Customer or company name

**Optional but recommended:**
- `email` - For customer lookup
- `country` - For geographic segmentation
- `created` - When they became a customer
- `metadata` - Custom fields for segmentation (industry, size, etc.)

### 2. Plans

Your pricing structure.

```json
{
  "id": "plan_pro_monthly",
  "name": "Pro Monthly",
  "interval": "month",
  "interval_count": 1,
  "currency": "USD"
}
```

**Required fields:**
- `id` - Your unique identifier for this plan
- `name` - Display name
- `interval` - Billing period: `day`, `week`, `month`, `year`
- `interval_count` - Number of intervals (1 for monthly, 12 for annual)
- `currency` - Three-letter currency code

**Why interval matters:** GrowPanel uses the interval to normalize revenue to monthly. A $1,200 annual plan becomes $100 MRR.

### 3. Invoices

The actual payment records that drive all metrics.

```json
{
  "id": "inv_456",
  "customer_id": "cust_123",
  "plan_id": "plan_pro_monthly",
  "type": "subscription",
  "invoice_date": "2024-02-01T00:00:00Z",
  "amount": 9900,
  "currency": "USD",
  "period_start": "2024-02-01T00:00:00Z",
  "period_end": "2024-03-01T00:00:00Z"
}
```

**Required fields:**
- `id` - Your unique identifier for this invoice
- `customer_id` - Must match an existing customer
- `type` - One of: `subscription`, `one-time`, `refund`, `cancellation`
- `invoice_date` - When the invoice was created
- `amount` - Amount in cents (9900 = $99.00)
- `currency` - Three-letter currency code

**Required for subscription invoices:**
- `plan_id` - Must match an existing plan

**Recommended:**
- `period_start` and `period_end` - The billing period this invoice covers
- `subscription_id` - Groups invoices into a subscription

---

## Invoice types explained

The `type` field tells GrowPanel how to process the invoice:

### subscription

Regular recurring invoices. These create MRR.

```json
{
  "type": "subscription",
  "amount": 9900,
  "plan_id": "plan_pro"
}
```

### one-time

Setup fees, consulting, or other non-recurring charges. These don't affect MRR.

```json
{
  "type": "one-time",
  "amount": 50000
}
```

### refund

Partial or full refunds. Reduces MRR proportionally.

```json
{
  "type": "refund",
  "amount": -4950
}
```

### cancellation

Marks the end of a subscription. Use amount `0` to signal churn without a refund.

```json
{
  "type": "cancellation",
  "amount": 0,
  "customer_id": "cust_123"
}
```

---

## Common scenarios

### New customer signs up

1. Create the customer
2. Create the plan (if not already exists)
3. Create a `subscription` invoice

```bash
# 1. Create customer
curl -X POST https://api.growpanel.io/v1/data-sources/{id}/customers \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{"id": "cust_new", "name": "New Customer", "email": "new@example.com"}'

# 2. Create invoice
curl -X POST https://api.growpanel.io/v1/data-sources/{id}/invoices \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "id": "inv_001",
    "customer_id": "cust_new",
    "plan_id": "plan_pro",
    "type": "subscription",
    "invoice_date": "2024-02-01T00:00:00Z",
    "amount": 9900,
    "currency": "USD"
  }'
```

**Result:** GrowPanel records this as **New MRR** of $99.

### Customer upgrades

Send a new `subscription` invoice with the higher amount.

```json
{
  "id": "inv_002",
  "customer_id": "cust_123",
  "plan_id": "plan_enterprise",
  "type": "subscription",
  "amount": 29900,
  "invoice_date": "2024-03-01T00:00:00Z"
}
```

**Result:** GrowPanel compares to the previous invoice ($99) and records **Expansion MRR** of $200.

### Customer downgrades

Send a new `subscription` invoice with the lower amount.

```json
{
  "id": "inv_003",
  "customer_id": "cust_123",
  "plan_id": "plan_basic",
  "type": "subscription",
  "amount": 4900,
  "invoice_date": "2024-04-01T00:00:00Z"
}
```

**Result:** GrowPanel records **Contraction MRR** of $250.

### Customer cancels

Send a `cancellation` invoice with amount `0`.

```json
{
  "id": "inv_004",
  "customer_id": "cust_123",
  "type": "cancellation",
  "amount": 0,
  "invoice_date": "2024-05-01T00:00:00Z"
}
```

**Result:** GrowPanel records **Churned MRR** equal to their last invoice amount.

### Customer reactivates

After a customer has churned, send a new `subscription` invoice.

```json
{
  "id": "inv_005",
  "customer_id": "cust_123",
  "plan_id": "plan_pro",
  "type": "subscription",
  "amount": 9900,
  "invoice_date": "2024-08-01T00:00:00Z"
}
```

**Result:** GrowPanel recognizes this customer previously churned and records **Reactivation MRR** of $99.

---

## Historical data import

For accurate reporting, import your full invoice history—not just current subscriptions.

### Why history matters

GrowPanel calculates:
- **Cohort retention** - Needs to know when customers started
- **Churn trends** - Needs to see when customers left
- **Growth decomposition** - Needs the full sequence of changes

Without historical data, you'll only see current state, not trends.

### Import order

1. **Customers first** - Invoices reference customers
2. **Plans second** - Invoices reference plans
3. **Invoices last** - In chronological order

### Batch imports

For large datasets, use batch requests:

```json
{
  "invoices": [
    {"id": "inv_001", "customer_id": "cust_1", "amount": 9900, "...": "..."},
    {"id": "inv_002", "customer_id": "cust_2", "amount": 4900, "...": "..."},
    {"id": "inv_003", "customer_id": "cust_1", "amount": 9900, "...": "..."}
  ]
}
```

---

## Real-time sync

After the initial import, keep GrowPanel updated as events happen:

| Event in your system | Action in GrowPanel |
|---------------------|---------------------|
| New subscription | POST customer + invoice |
| Renewal | POST invoice |
| Upgrade/downgrade | POST invoice with new amount |
| Cancellation | POST cancellation invoice |
| Refund | POST refund invoice |

### Webhook-driven sync

If your billing system supports webhooks, trigger API calls on:

- `invoice.paid` → POST invoice
- `subscription.created` → POST customer (if new) + invoice
- `subscription.updated` → POST invoice with new amount
- `subscription.cancelled` → POST cancellation invoice
- `charge.refunded` → POST refund invoice

---

## Best practices

### Use stable IDs

Your `id` fields should be:
- **Unique** - No duplicates within a type
- **Stable** - Same customer always has the same ID
- **Immutable** - Don't change IDs over time

GrowPanel uses IDs for deduplication. If you send the same invoice ID twice, it updates rather than duplicates.

### Include all invoices

Send every paid invoice, including:
- Historical invoices (for accurate cohort analysis)
- Prorated invoices (GrowPanel handles proration)
- Zero-amount invoices (for trials converting to paid)

### Use cents for amounts

All amounts should be in the smallest currency unit:
- $99.00 → `9900`
- €49.50 → `4950`
- ¥1000 → `1000`

### Include period dates

`period_start` and `period_end` help GrowPanel:
- Handle partial-month invoices correctly
- Align billing cycles across customers
- Calculate accurate daily MRR

---

## API reference

For complete endpoint documentation, browse the [interactive API reference](https://api.growpanel.io/docs). The data-management endpoints live under the **Data — Customers**, **Data — Plans**, **Data — Invoices**, and **Data — Plan Groups** sections in the sidebar — each shows request/response schemas, ready-to-paste SDK examples (JavaScript / Python / Go), and a try-it-out panel for your own API key.

---

## Need help?

Building an integration and have questions?

- **Support:** Use the support widget or email hello@growpanel.io
- **Include:** Your use case, billing platform, and any specific questions

We're happy to help you connect your billing system to GrowPanel.

---

## CLI
<!-- url: https://growpanel.io/developers/cli -->

The GrowPanel CLI lets you access your subscription analytics, manage data, and configure settings directly from the terminal. It's designed for scripting, automation, and AI agents.

---

## Installation

Install globally via npm (requires Node.js 20 or later):

```bash
npm install -g growpanel-cli
```

Verify the installation:

```bash
growpanel --version
```

---

## Authentication

You need an API key from your GrowPanel dashboard. Go to **Settings > API Keys** and create one. See [Authentication](/developers/rest-api/authentication/) for details.

Configure the key using one of these methods (in priority order):

### CLI flag (highest priority)

```bash
growpanel --api-key YOUR_API_KEY reports summary
```

### Environment variable

```bash
export GROWPANEL_API_KEY=YOUR_API_KEY
```

### Config file

Create `~/.growpanelrc`:

```json
{
    "api_key": "YOUR_API_KEY"
}
```

---

## Quick start

```bash
# Get a summary of your key metrics
growpanel reports summary

# Get MRR timeline for 2024, broken down by month
growpanel reports mrr --date 20240101-20241231 --interval month

# List your customers
growpanel customers list

# Export all customers as CSV
growpanel exports customers > customers.csv
```

---

## Output formats

The CLI auto-detects the best format based on context:

- **Terminal** — Colored tables with formatted numbers
- **Piped or redirected** — Raw JSON for machine consumption

Override the default with `--format`:

```bash
growpanel reports summary --format json
growpanel reports summary --format table
growpanel reports summary --format csv
```

---

## Global options

These options work with any command:

| Option | Description |
|--------|-------------|
| `--api-key <key>` | API key (overrides env var and config file) |
| `--api-url <url>` | API base URL (default: `https://api.growpanel.io`) |
| `--format <fmt>` | Output format: `json`, `table`, or `csv` |
| `--no-color` | Disable colored output |
| `--verbose` | Log HTTP requests and responses to stderr |
| `--version` | Show version number |
| `--help` | Show help for any command |

---

## Reports

Fetch any subscription analytics report by name. New reports added to the API work automatically without a CLI update.

```bash
growpanel reports <name> [options]
```

### Report options

| Option | Description |
|--------|-------------|
| `--date <range>` | Date range in `yyyyMMdd-yyyyMMdd` format |
| `--interval <val>` | Aggregation: `day`, `week`, `month`, `quarter`, `year` |
| `--currency <code>` | Filter by currency code (e.g., `usd`, `eur`) |
| `--region <region>` | Filter by geographic region |
| `--plan <id>` | Filter by plan group ID |
| `--country <code>` | Filter by ISO country code |
| `--data-source <id>` | Filter by data source ID |
| `--breakdown <field>` | Group by: `plan`, `currency`, `billing_freq` |

### Available reports

`mrr`, `mrr-table`, `mrr-table-subtypes`, `summary`, `cmrr-summary`, `movement-table`, `map`, `cohort`, `leads`, `leads-table`, `leads-days`, `leads-summary`, `transactions`, `transactions-table`, `transactions-detail`, `transactions-summary`, `invoices-detail`, `churn-scheduled`, `churn-scheduled-movements`, `churn-scheduled-summary`, `customer-concentration`, `cashflow-failed-payments`, `cashflow-failed-payments-summary`, `cashflow-failed-payments-detail`, `cashflow-failed-payments-table`, `cashflow-refunds`, `cashflow-refunds-table`, `cashflow-refunds-detail`, `cashflow-failure-rate`, `cashflow-failure-rate-summary`, `cashflow-outstanding-unpaid`, `custom-variables`

### Examples

```bash
# MRR over the last year, grouped by month
growpanel reports mrr --date 20240101-20241231 --interval month

# Summary metrics as JSON
growpanel reports summary --format json

# Cohort retention filtered to USD customers
growpanel reports cohort --currency usd

# MRR broken down by plan
growpanel reports mrr --breakdown plan

# Failed payments for Q4
growpanel reports cashflow-failed-payments --date 20241001-20241231
```

---

## Customers

```bash
# List all customers
growpanel customers list
growpanel customers list --limit 50 --offset 100

# Get a specific customer
growpanel customers get cus_abc123

# Trigger a resync (admin/owner only)
growpanel customers resync cus_abc123
```

---

## Plans

```bash
growpanel plans list
```

---

## Data management

Generic CRUD operations for data resources. Supported resources: `customers`, `plans`, `plan-groups`, `data-sources`, `invoices`.

### Standard CRUD

```bash
growpanel data customers list
growpanel data customers get cus_abc123
growpanel data customers create --body '{"id":"cus_new","name":"Acme Inc","email":"billing@acme.com"}'
growpanel data customers update cus_abc123 --body '{"name":"Updated Name"}'
growpanel data customers delete cus_abc123
```

### Data source operations

```bash
growpanel data data-sources list
growpanel data data-sources reset ds_123
growpanel data data-sources connect ds_123
growpanel data data-sources full-import ds_123
growpanel data data-sources progress ds_123
growpanel data data-sources abort ds_123
```

### Plan group operations

```bash
growpanel data plan-groups list
growpanel data plan-groups ai-suggest --body '{"plans":["plan1","plan2"]}'
growpanel data plan-groups merge --body '{"source":"pg1","target":"pg2"}'
growpanel data plan-groups delete-multiple --body '{"ids":["pg1","pg2"]}'
```

---

## Exports

Export data as CSV. Output goes directly to stdout, so redirect to a file:

```bash
growpanel exports customers > customers.csv
growpanel exports mrr-movements > movements.csv
growpanel exports customers --date 20240101-20241231 > customers_2024.csv
```

---

## Settings

```bash
# Get all account settings
growpanel settings get

# Update settings
growpanel settings update --body '{"base_currency":"usd"}'

# Integration settings (GET without --body, update with --body)
growpanel settings notifications
growpanel settings notifications --body '{"email_reports":true}'
growpanel settings stripe
growpanel settings hubspot
growpanel settings webhook
growpanel settings slack
growpanel settings teams
growpanel settings looker-studio
```

---

## Integrations

Manage webhook subscriptions for third-party platforms like n8n or Zapier.

```bash
growpanel integrations verify
growpanel integrations webhooks list
growpanel integrations webhooks get wh_123
growpanel integrations webhooks create --body '{"url":"https://example.com/hook","events":["customer.created"]}'
growpanel integrations webhooks delete wh_123
growpanel integrations webhooks sample customer.created
```

---

## Account management

### Account

```bash
growpanel account get
growpanel account update --body '{"name":"New Account Name"}'
```

### Billing

```bash
growpanel account billing details
growpanel account billing invoices
growpanel account billing portal
growpanel account billing subscribe --body '{"plan":"pro"}'
growpanel account billing change-plan --body '{"plan":"enterprise"}'
growpanel account billing undo-cancellation
```

### Team

```bash
growpanel account team list
growpanel account team invite --body '{"email":"user@example.com","role":"admin"}'
growpanel account team edit user_123 --body '{"role":"readonly"}'
growpanel account team remove user_123
```

### API keys

```bash
growpanel account api-keys list
growpanel account api-keys create --body '{"name":"CI Key","role":"readonly"}'
growpanel account api-keys update key_123 --body '{"name":"Renamed"}'
growpanel account api-keys delete key_123
```

---

## Generic API passthrough

Call any API endpoint directly. Useful for new endpoints or one-off requests:

```bash
# GET with query parameters
growpanel api GET /reports/mrr date=20240101-20250131 interval=month

# POST with JSON body
growpanel api POST /data/customers --body '{"name":"Acme","email":"a@b.com"}'

# DELETE
growpanel api DELETE /data/customers/cus_123
```

---

## Request body input

Commands that accept a request body support three input methods:

```bash
# Inline JSON
growpanel data customers create --body '{"name":"Acme"}'

# From a file
growpanel data customers create --body @customer.json

# Piped from stdin
echo '{"name":"Acme"}' | growpanel data customers create
cat customer.json | growpanel data customers create
```

---

## Usage with AI agents

The CLI is optimized for AI agents and automation:

- JSON output by default when piped (no extra flags needed)
- Consistent error messages on stderr, data on stdout
- Exit code 0 on success, 1 on error
- All commands are non-interactive
- Detailed `--help` on every command
- New API endpoints work automatically via `growpanel api` passthrough

```bash
# AI agent workflow
export GROWPANEL_API_KEY=your-key

# Get structured data
growpanel reports summary --format json

# Chain with jq
growpanel reports mrr --format json | jq '.[0].total_mrr'

# Pipe CSV to other tools
growpanel exports customers | csvtool col 1,2 -
```

---

## Need help?

- Run `growpanel --help` or `growpanel <command> --help` for usage details
- Check the [API documentation](/developers/rest-api/) for endpoint specifics
- Review [Authentication](/developers/rest-api/authentication/) for API key setup
- Use the support widget for help

---

## Developers
<!-- url: https://growpanel.io/developers/ -->

Build integrations, access your data programmatically, and extend GrowPanel to fit your workflow.

---

## Build your own integration

Don't see your billing platform in our native integrations? You can connect any billing system to GrowPanel using our Import Data API.

**[Building integrations](/developers/building-integrations/)** - Learn how GrowPanel processes subscription data and how to push customers, plans, and invoices from any billing system.

This is the recommended starting point if you want to connect a custom billing system, in-house platform, or any service we don't natively integrate with.

---

## REST API

Access your subscription data and reports programmatically.

**[REST API documentation](/developers/rest-api/)** - Complete API reference including authentication, endpoints, and rate limits.

| Resource | Description |
|----------|-------------|
| [Authentication](/developers/rest-api/authentication/) | API keys and access levels |
| [Interactive API reference](https://api.growpanel.io/docs) | All endpoints with try-it-out, schemas, and SDK code samples |
| [Error codes](/developers/rest-api/error-codes/) | HTTP status codes and troubleshooting |
| [Rate limiting](/developers/rest-api/rate-limiting/) | Request limits and best practices |

### Quick example

```bash
curl https://api.growpanel.io/reports/summary \
  -H "Authorization: Bearer YOUR_API_KEY"
```

---

## Official SDKs

Use a typed SDK instead of raw HTTP. All three stay in lock-step with the API — auto-generated from the same OpenAPI spec on every deploy.

| SDK | Install | Docs |
|-----|---------|------|
| [JavaScript / TypeScript](/developers/sdks/javascript/) | `npm install @growpanel/sdk` | Guide + reference |
| [Python](/developers/sdks/python/) | `pip install growpanel` | Guide + reference |
| [Go](/developers/sdks/go/) | `go get github.com/growpanel/growpanel-sdk-go` | Guide + reference |

```typescript
import { GrowPanel } from '@growpanel/sdk';
const gp = new GrowPanel({ apiKey: process.env.GROWPANEL_API_KEY });
const summary = await gp.reports.getSummary();
```

---

## MCP Server

Let AI assistants access your GrowPanel data through natural language.

**[MCP Server documentation](/developers/mcp-server/)** - Connect Claude, Cursor, and other AI tools to your subscription analytics.

The MCP (Model Context Protocol) server runs locally and provides tools for querying:

| Tool | What you can ask |
|------|------------------|
| `getMRR` | MRR, ARR, churn, LTV, ARPA |
| `getLeads` | Leads, trials, conversion rates |
| `getCohorts` | Cohort retention analysis |

### Quick start

```bash
npm install -g growpanel-mcp-server
export GROWPANEL_API_TOKEN=sk_live_xxx
growpanel-mcp
```

---

## CLI

Access your subscription analytics and manage your account from the terminal. Designed for scripting, automation, and AI agents.

**[CLI documentation](/developers/cli/)** - Installation, authentication, and full command reference.

```bash
npm install -g growpanel-cli
growpanel reports summary
```

| Command | Description |
|---------|-------------|
| `reports <name>` | Fetch any analytics report (MRR, churn, cohort, etc.) |
| `customers` | List and query customers |
| `data <resource>` | CRUD for customers, plans, invoices, data sources |
| `exports` | Export data as CSV |
| `settings` | Manage account and integration settings |
| `api <method> <path>` | Raw API passthrough for any endpoint |

---

## Need help?

- Check the [interactive API reference](https://api.growpanel.io/docs) for endpoint details
- Review [error codes](/developers/rest-api/error-codes/) for troubleshooting
- Use the support widget for help

---

## MCP Server
<!-- url: https://growpanel.io/developers/mcp-server -->

The GrowPanel MCP Server lets AI assistants like **Claude.ai**, **ChatGPT**, **Claude Desktop**, **Cursor**, and **Windsurf** access your subscription analytics through natural language. Ask "What's my current MRR?" or "Show me last quarter's churn by plan" directly in your AI client — the assistant talks to the GrowPanel API on your behalf.

It's built on the [Model Context Protocol](https://modelcontextprotocol.io) (MCP), an open standard for connecting AI assistants to external data sources.

---

## What you can ask

Once connected, your AI assistant has access to **14 tools** spanning analytics, customer data, settings, billing, team management, and more — plus a generic API passthrough so any new GrowPanel API feature is immediately available without updating the server.

A few realistic prompts:

- *"What was my MRR in Q1, broken down by plan?"*
- *"Show me my top 20 customers by MRR."*
- *"Which customers churned in the last 90 days with more than $500 MRR?"*
- *"Pull the cohort retention matrix for customers who started in 2025."*
- *"Find subscribers in the EU on monthly billing — export that as CSV."*
- *"What's my current churn rate? How does it compare to last quarter?"*

The full tool list is at the bottom of this page.

---

## Quick start

There are three ways to connect, in order of ease:

- **Claude.ai or ChatGPT (one-click OAuth)** — paste a single URL, click Allow on the consent screen. No API key, no config files. Recommended for any client that supports remote MCP custom connectors.
- **Hosted with API key** — for clients that support remote MCP servers but require a Bearer token in the connector config (older versions, some IDEs).
- **Self-hosted (local)** — run the open-source `growpanel-mcp-server` package on your machine. Useful if your client only supports local stdio MCP, or if you want everything inside your own network.

All three options expose the **exact same 14 tools**.

---

## Option 1 — Claude.ai or ChatGPT (one-click OAuth) — recommended

The hosted server at `https://mcp.growpanel.io` supports OAuth 2.1 with Dynamic Client Registration. From your point of view, that means: paste the URL, click Connect, log in to GrowPanel as you normally do, click Allow. Done.

### Steps

1. Open your AI client's connector settings:
   - **Claude.ai:** [claude.ai/settings/connectors](https://claude.ai/settings/connectors) → "Add custom connector"
   - **ChatGPT:** Settings → Connectors → "Add custom connector"
2. Paste the URL: `https://mcp.growpanel.io`
3. Click **Connect**
4. You'll be redirected to GrowPanel. If you're not already logged in, sign in with whatever method you normally use (Google, Microsoft, email/password)
5. If you have access to multiple GrowPanel accounts, choose which one to connect
6. Review the consent screen and click **Allow**
7. You're redirected back to your AI client. Connected.

No API key, no client ID, no copy/paste of any credentials.

### What the AI client can do

The connection grants the AI client access to your selected GrowPanel account on your behalf — same as if you'd generated an admin-level API key. It can read all reports, list and modify customers/plans/data sources, manage settings and integrations, and use the generic `api_request` tool to hit any endpoint.

If you have access to multiple GrowPanel accounts, your AI client will only have access to the **one specific account** you chose during the consent flow. To connect another account, repeat the flow — each grant becomes its own entry in your connections list.

### Managing and revoking access

Visit [Settings → Integrations](https://app.growpanel.io/settings/integrations) in GrowPanel — the MCP Server card lists every AI assistant currently connected. From there you can:

- See every AI client currently connected to your account, and which GrowPanel account each grant is scoped to
- See when each connection was created and last used
- Revoke a connection — the AI client loses access immediately and the underlying API key is deleted

You can also revoke from the AI client's side (e.g. delete the connector in Claude.ai) — both methods work; either way the underlying access is gone.

### Verify it's working

In a new chat, ask:

> *Can you list the GrowPanel tools you have available?*

Your assistant should respond with the 14 tools (`get_report`, `list_customers`, etc.). If it doesn't, see [Troubleshooting](#troubleshooting).

---

## Option 2 — Hosted with API key

For AI clients that support remote MCP servers but require a Bearer token in the connector config (some IDE plugins, older client versions). The hosted server lives at `https://mcp.growpanel.io` — give your AI client that URL plus your API key as a Bearer token.

### Prerequisites

- **A GrowPanel API key.** Generate one in **[Settings → API Keys](https://app.growpanel.io/settings/api-keys)**. Copy it — you'll need it in a moment.

### Configure your client

Add a custom connector / remote MCP server with:

- **URL:** `https://mcp.growpanel.io`
- **Authentication:** Bearer token (header `Authorization: Bearer your-api-key-here`)

The exact menu varies by client — look for "Connectors", "Custom MCP", "Remote MCP server", or similar in your AI client's settings.

### For clients that only support local MCP (config file with command + args)

Use the [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) bridge, which presents a remote MCP server as a local stdio server:

```json
{
    "mcpServers": {
        "growpanel": {
            "command": "npx",
            "args": [
                "-y",
                "mcp-remote",
                "https://mcp.growpanel.io",
                "--header",
                "Authorization:Bearer your-api-key-here"
            ]
        }
    }
}
```

Save the file, restart your AI client, and you're connected. No GrowPanel-specific install — `mcp-remote` is a generic bridge maintained by the MCP community.

---

## Option 3 — Self-hosted (local)

Run the open-source server locally. Requires **Node.js 20 or newer** (`node --version`; install from [nodejs.org](https://nodejs.org/) if missing).

### Add the server to your client config

The fastest setup uses `npx`, which downloads and runs the server on demand — no install step needed.

```json
{
    "mcpServers": {
        "growpanel": {
            "command": "npx",
            "args": ["-y", "growpanel-mcp-server"],
            "env": {
                "GROWPANEL_API_KEY": "your-api-key-here"
            }
        }
    }
}
```

Replace `your-api-key-here` with the API key you copied earlier. Save the file and restart your client.

### Install globally (skip the npx download on each start)

If you'd rather have the server installed permanently:

```bash
npm install -g growpanel-mcp-server
```

Then change `command` from `npx` to `growpanel-mcp` and remove the `args` line:

```json
{
    "mcpServers": {
        "growpanel": {
            "command": "growpanel-mcp",
            "env": {
                "GROWPANEL_API_KEY": "your-api-key-here"
            }
        }
    }
}
```

Re-run `npm install -g growpanel-mcp-server` to update when a new version ships.

### Run from source

For development or to inspect the code:

```bash
git clone https://github.com/growpanel/growpanel-mcp-server.git
cd growpanel-mcp-server
npm install
npm run build
```

Then point `command` at the local build path and provide the API key as before.

---

## Available tools

The server exposes 14 tools. Your AI assistant decides which to call based on your question.

### Analytics

| Tool | What it does |
|---|---|
| `get_report` | Fetches any analytics report by name — mrr, summary, cohort, leads, cashflow, churn-scheduled, customer-concentration, and ~25 more. New reports added to the API work automatically. |

### Customers and plans

| Tool | What it does |
|---|---|
| `list_customers` | Lists customers with MRR, subscription status, plan, country, and other identifying metadata. |
| `get_customer` | Detailed view of one customer, including current subscriptions and recent movements. |
| `resync_customer` | Triggers a fresh data sync from the billing provider for one customer. |
| `list_plans` | Returns all plans and plan groups. |

### Data management

| Tool | What it does |
|---|---|
| `manage_data` | Create, read, update, delete: customers, plans, plan groups, data sources, invoices. |
| `export_csv` | Exports customers or MRR movements as a CSV file. |

### Settings and integrations

| Tool | What it does |
|---|---|
| `manage_settings` | Read or update account and integration settings (currency, churn recognition, Slack, HubSpot, etc.). |
| `manage_webhooks` | List, create, verify, and delete webhook subscriptions. |

### Account, billing, team

| Tool | What it does |
|---|---|
| `manage_account` | Read or update your GrowPanel account record. |
| `manage_billing` | Billing details, invoices, subscriptions, payment methods on your GrowPanel account. |
| `manage_team` | List team members, invite new ones, edit roles, or remove access. |
| `manage_api_keys` | List, create, update, or delete API keys. |

### Generic passthrough

| Tool | What it does |
|---|---|
| `api_request` | Call any GrowPanel API endpoint directly — method, path, params, body. New API features are available immediately without updating the MCP server. |

---

## How it works

When you ask your AI assistant a question:

1. The assistant decides which tool(s) to call.
2. The MCP server — either the hosted one at `mcp.growpanel.io` or your local copy — receives the call with your authentication.
3. The server forwards an authenticated request to `api.growpanel.io` with the credentials for your selected GrowPanel account.
4. The response goes back to your AI assistant in a structured format.
5. The assistant interprets the data and replies in plain language.

In hosted mode the server runs on GrowPanel's infrastructure (Cloudflare Workers, same account that hosts the rest of GrowPanel). In self-hosted mode it runs on your machine. Either way, your credentials only travel to GrowPanel — never to Anthropic, OpenAI, Cursor, or any other third party.

### OAuth specifics (Option 1)

The hosted server implements OAuth 2.1 with PKCE (proof key for code exchange) and supports Dynamic Client Registration (RFC 7591), Protected Resource Metadata (RFC 9728), and Authorization Server Metadata (RFC 8414). That's why "Add custom connector" needs no manual config: your AI client auto-discovers the OAuth endpoints, registers itself, and walks you through the flow.

When you click Allow on the consent screen, GrowPanel issues your AI client an access token bound to one (user, account, client) triple. The token expires after 30 days; refresh tokens last 90 days and rotate on each use. The MCP server internally maps that token to a real GrowPanel API key for that account, so the rest of the API needs no changes.

Revoking a connection (via the MCP Server card on [Settings → Integrations](https://app.growpanel.io/settings/integrations) or from the AI client's side) deletes both the OAuth token and the underlying API key.

---

## Filtering and parameters

All tools accept the same filters and parameters as the [GrowPanel REST API](/developers/rest-api/). Common ones:

| Parameter | Format | Example |
|---|---|---|
| `date` | `yyyyMMdd-yyyyMMdd` (range) or `yyyyMMdd` (single) | `date=20260101-20260331` |
| `interval` | `day`, `week`, `month`, `quarter`, `year` | `interval=month` |
| `region` | Region key or country code | `region=europe` |
| `plan` | Plan id | `plan=plan_pro` |
| `currency` | ISO 4217 lowercase | `currency=usd` |

Multi-value OR depends on the filter:

- **`region`** (which accepts both region keys and country codes) is space-separated, e.g. `region=gb+fr` (`+` is URL-encoded space) or `region=europe+us`.
- **All other filters** (`plan`, `billing_freq`, `currency`, `custom_<name>`, etc.) use `~~`, e.g. `plan=pro~~enterprise`.

Multi-filter AND just uses separate keys.

You don't need to remember these — your AI assistant will translate natural language ("last quarter for EU customers") into the correct parameters.

---

## Troubleshooting

### OAuth flow: "No GrowPanel account access"

You're logged in but your user has no GrowPanel accounts. Check that you've completed signup and joined or created an account at [app.growpanel.io](https://app.growpanel.io/), then retry.

### OAuth flow: stuck on the GrowPanel app after login

If clicking Connect sends you to GrowPanel and you end up on the dashboard instead of returning to your AI client, the OAuth intent cookie didn't propagate. Try the flow once more — it should pick up automatically. If it persists, sign out of GrowPanel completely, then start the connector flow fresh.

### Tool calls return "401 Unauthorized" after connecting

Your access token has expired or been revoked. Disconnect the connector in your AI client and reconnect — that triggers a fresh OAuth flow.

### "growpanel-mcp not found" or "command not found" (self-hosted)

You're using the global install but Node's binary directory isn't on your PATH. Either switch to the `npx` config above (recommended), or run `npm bin -g` to find the path and add it to your shell's PATH.

### Server connects but the AI client can't see tools

1. **Restart your AI client completely.** Some clients only re-read MCP config on full restart, not on reload.
2. **Check the config file syntax.** A missing comma or quote in your client's MCP config silently breaks all MCP servers. Paste it through a JSON validator if unsure.
3. **Check your client's MCP logs.** Most AI clients write MCP server logs to a known location — search the client's docs for "MCP logs". The log shows whether the server connected and which method calls failed.

### Hosted server with API key: "401 Unauthorized" from `mcp.growpanel.io`

The Bearer token isn't reaching us, or it's invalid:

- Test the URL with curl:
  ```bash
  curl -X POST https://mcp.growpanel.io \
       -H "Authorization: Bearer your-api-key-here" \
       -H "Content-Type: application/json" \
       -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
  ```
  A 200 response with a list of tools means the URL and key both work — the issue is in your client's connector config.
- For `mcp-remote`-bridged clients, double-check the `--header` argument has no extra spaces around `:`.

### Self-hosted authentication errors ("401 Unauthorized")

Your API key isn't being read or is invalid:

- Make sure the key is set under `env` in the config, not somewhere else.
- Verify the key works by testing with `curl`:
  ```bash
  curl -H "Authorization: Bearer your-api-key-here" \
       https://api.growpanel.io/v2/account
  ```
- Generate a fresh key at [Settings → API Keys](https://app.growpanel.io/settings/api-keys) if needed.

### "Node version too old" (self-hosted)

The server needs Node 20+. Check with `node --version`. If you have an older version, update via [nodejs.org](https://nodejs.org/) or use a version manager like `nvm`.

### npx is slow on first run (self-hosted)

That's normal — npx downloads the package the first time. Subsequent starts use the cached copy and are instant. If you find this annoying, switch to the global-install option.

### Tools return empty results

Check the date range. Many GrowPanel reports default to a recent window; if your account's data is historical, ask your assistant to widen the range explicitly ("for all of 2025" rather than "for the last 30 days").

### Still stuck

Use the support widget on this site, or reach out at [hello@growpanel.io](mailto:hello@growpanel.io). Include your AI client name, the exact error, and a snippet of your config (with credentials redacted).

---

## Security

- **Credentials only travel between you, GrowPanel, and (in hosted mode) `mcp.growpanel.io`** — never to Anthropic, OpenAI, Cursor, or any other third party. The hosted MCP server is a thin protocol adapter operated by GrowPanel; it forwards your authentication to `api.growpanel.io` per request.
- **OAuth (Option 1) issues per-grant tokens.** Each AI client gets a separate token scoped to one specific GrowPanel account. Tokens rotate (refresh tokens live 90 days, access tokens 30 days). Revoking a connection from the MCP Server card on [Settings → Integrations](https://app.growpanel.io/settings/integrations) deletes both the OAuth token and the underlying API key the server held on your behalf.
- **API keys (Options 2 and 3) are scoped to your account.** Use a read-only key (Settings → API Keys → role: Read-only) if you only need to query data without making changes.
- **Rotate keys periodically.** If you suspect an API key has been exposed, revoke it in Settings → API Keys and generate a new one.
- **Self-hosted mode keeps everything local.** If even the hosted relay is too much exposure, run the local server — your key never leaves your machine.

---

## Source code

The MCP server is open source: [github.com/growpanel/growpanel-mcp-server](https://github.com/growpanel/growpanel-mcp-server). Issues and PRs welcome.

---

## Related

- [REST API documentation](/developers/rest-api/) — full reference for the underlying API
- [Authentication](/developers/rest-api/authentication/) — API key creation and scoping
- [GrowPanel CLI](/developers/cli/) — the command-line equivalent if you'd rather script directly

---

## API Authentication
<!-- url: https://growpanel.io/developers/rest-api/authentication -->

All GrowPanel API requests require authentication using an API key.

---

## Obtain an API key

1. Click the account/profile menu in the upper right corner
2. Click **API Keys**
3. Click the **Create API key** button

See [API Keys](/docs/account-and-billing/api-keys/) for detailed instructions on managing your keys.

---

## Access levels

API keys can have different access levels:

| Level | Capabilities |
|-------|--------------|
| **admin** | Full access including settings, team management, and data modifications |
| **readonly** | Read-only access to reports and data |

---

## Store your key securely

After creation, the key is shown **once**. Copy it to a secure location:

- Password manager
- Secrets vault (AWS Secrets Manager, HashiCorp Vault)
- Encrypted environment variables

The key cannot be retrieved again. If you lose it, you'll need to regenerate or create a new key.

---

## Using the API key

GrowPanel uses Bearer Authentication. Include your API key in the `Authorization` HTTP header:

```bash
curl https://api.growpanel.io/v1/reports/mrr \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Example header format:

```
Authorization: Bearer xxxxxxxx-b4ad-4b05-xxxx-76ed42c6e692.xxxxxxxx-5ff6-4862-xxxx-a992a789ea60
```

---

## Related pages

- [API Keys management](/docs/account-and-billing/api-keys/) - Creating and managing keys
- [Interactive API reference](https://api.growpanel.io/docs) - Complete endpoint documentation
- [Error codes](/developers/rest-api/error-codes/) - Authentication errors

---

## API Error Codes
<!-- url: https://growpanel.io/developers/rest-api/error-codes -->

When an API call results in an error, GrowPanel returns an HTTP status code along with an error message.

---

## Error response format

```json
{
  "message": "Device parameter missing"
}
```

---

## HTTP status codes

| Status code | Description |
|-------------|-------------|
| 400 | Bad Request. Maybe you're missing some parameters |
| 401 | Unauthorized. Your API key may be invalid. |
| 403 | Forbidden. Your API key does not have access to the resource. |
| 404 | The resource was not found. |
| 405 | Method (GET, POST, PUT, DELETE) not allowed. |
| 429 | Too many requests. Your request has been [rate limited](/developers/rest-api/rate-limiting/) |
| 500 | Internal server error. If this error persists, get in touch with us. |
| 501-599 | A problem occurred connecting to the GrowPanel platform. Try again later. |

---

## Common errors and solutions

### 401 Unauthorized

**Cause:** Invalid or missing API key.

**Solution:**
1. Verify your API key is correct
2. Ensure the `Authorization: Bearer YOUR_API_KEY` header is included
3. Check if the key was regenerated or deleted

### 403 Forbidden

**Cause:** Your API key doesn't have permission for this resource.

**Solution:**
1. Verify the key has the correct access level (admin vs readonly)
2. Check if you're accessing resources from another account

### 429 Too Many Requests

**Cause:** Rate limit exceeded.

**Solution:**
1. Wait for the rate limit window to reset (usually 1 minute)
2. Implement exponential backoff in your integration
3. Reduce request frequency

See [Rate limiting](/developers/rest-api/rate-limiting/) for current limits.

---

## Related pages

- [Authentication](/developers/rest-api/authentication/) - Setting up API access
- [Rate limiting](/developers/rest-api/rate-limiting/) - Request limits
- [Interactive API reference](https://api.growpanel.io/docs) - Endpoint documentation

---

## REST API
<!-- url: https://growpanel.io/developers/rest-api/ -->

GrowPanel exposes its entire dataset and every dashboard report via a REST API. Pull metrics, push customers and invoices from your own systems, subscribe to events, and build automations.

**Base URL:** `https://api.growpanel.io`

<h3>→ [Open the interactive API reference](https://api.growpanel.io/docs)</h3>

Full endpoint catalogue with typed schemas, example requests, and try-it-out powered by your API key.

---

## Official SDKs

Use a typed SDK instead of building raw HTTP calls. All three are auto-generated from the same OpenAPI spec, so they stay in lock-step with the API.

| Language | Install | Repo |
|----------|---------|------|
| **JavaScript / TypeScript** | `npm install @growpanel/sdk` | [growpanel-sdk-js](https://github.com/growpanel/growpanel-sdk-js) |
| **Python** | `pip install growpanel` | [growpanel-sdk-python](https://github.com/growpanel/growpanel-sdk-python) |
| **Go** | `go get github.com/growpanel/growpanel-sdk-go` | [growpanel-sdk-go](https://github.com/growpanel/growpanel-sdk-go) |

The interactive reference shows ready-to-paste example code in all three languages for every endpoint.

---

## Concepts

- **[Authentication](/developers/rest-api/authentication/)** — issuing and using API keys
- **[Rate limiting](/developers/rest-api/rate-limiting/)** — request limits and back-off advice
- **[Error codes](/developers/rest-api/error-codes/)** — what each HTTP status means

---

## Quick example

Fetch your current MRR with cURL:

```bash
curl https://api.growpanel.io/reports/summary \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```json
{
    "summary": {
        "mrr_current": 125000,
        "arr_current": 1500000,
        "subscribers_current": 250,
        "arpa_current": 500
    },
    "currency": "usd"
}
```

Same call with the JavaScript SDK:

```javascript
import { GrowPanel } from '@growpanel/sdk';

const gp = new GrowPanel({ apiKey: process.env.GROWPANEL_API_KEY });
const summary = await gp.reports.getSummary();
console.log(summary.data.summary.mrr_current);
```

---

## Related

- [Building integrations](/developers/building-integrations/) — how to connect custom billing systems
- [Custom API data source](/docs/data-sources/custom-api/) — step-by-step setup guide
- [CLI](/developers/cli/) — terminal client wrapping the same API
- [MCP server](/developers/mcp-server/) — connect AI assistants to your data

---

## API Rate Limiting
<!-- url: https://growpanel.io/developers/rest-api/rate-limiting -->

To ensure a stable and reliable platform, the GrowPanel API applies rate limiting. When a client sends too many requests in a short period, the API responds with **HTTP 429 (Too Many Requests)**.

---

## Current limits

The API enforces the following per-account request limits:

| Method | Limit |
|--------|-------|
| **GET** | 300 requests per minute |
| **GET** (reports endpoints) | 30 requests per minute |
| **POST** | 100 requests per minute |
| **PUT** | 100 requests per minute |
| **DELETE** | 50 requests per minute |

These values may be adjusted over time as we optimize the platform.

---

## Rate limit response

When you exceed the limit, the API returns:

```
HTTP/1.1 429 Too Many Requests
```

```json
{
  "message": "Rate limit exceeded"
}
```

---

## Best practices

### Implement backoff

When you receive a 429 response, wait before retrying:

```javascript
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url, options);
    if (response.status !== 429) return response;

    const waitTime = Math.pow(2, i) * 1000; // Exponential backoff
    await new Promise(resolve => setTimeout(resolve, waitTime));
  }
  throw new Error('Rate limit exceeded after retries');
}
```

### Batch requests

Where possible, use batch endpoints to reduce the number of API calls:

- Use date ranges to fetch multiple days of data in one request
- Use array parameters to filter for multiple items at once

### Cache responses

Cache API responses locally when the data doesn't change frequently:

- Summary metrics (cache for minutes)
- Customer lists (cache for hours)
- Historical reports (cache indefinitely)

---

## Need higher limits?

If you need higher rate limits for your use case, use the support widget and include:

- Your account email
- Description of your integration
- Expected request volume

---

## Related pages

- [Error codes](/developers/rest-api/error-codes/) - Understanding API errors
- [Authentication](/developers/rest-api/authentication/) - API key setup
- [Interactive API reference](https://api.growpanel.io/docs) - Endpoint documentation

---

## Go SDK
<!-- url: https://growpanel.io/developers/sdks/go -->

Official Go SDK for the GrowPanel REST API. Distributed through Go modules — no registry, just `go get`. Source: [growpanel/growpanel-sdk-go](https://github.com/growpanel/growpanel-sdk-go).

---

## Installation

```bash
go get github.com/growpanel/growpanel-sdk-go
```

Requires Go 1.22+. The generated client uses only the standard library plus a thin runtime helper from `github.com/oapi-codegen/runtime`.

---

## Authentication

Get an API key from **app.growpanel.io → Account → API keys**. See [Authentication](/developers/rest-api/authentication/) for key scoping and rotation.

```go
import (
    "context"
    "os"
    growpanel "github.com/growpanel/growpanel-sdk-go"
)

gp, err := growpanel.New(os.Getenv("GROWPANEL_API_KEY"))
if err != nil {
    log.Fatal(err)
}
```

Override the base URL or HTTP client:

```go
gp, err := growpanel.New(
    os.Getenv("GROWPANEL_API_KEY"),
    growpanel.WithBaseURL("https://api-dev.growpanel.io"),
    growpanel.WithHTTPClient(&http.Client{Timeout: 30 * time.Second}),
)
```

---

## Surfaces

`v0.1` ships a thin wrapper around the generated low-level client. Every endpoint in the OpenAPI spec is reachable as a typed method. Two flavours are exposed:

| Field | Use when |
|-------|----------|
| `gp.WithResponses.*` | You want typed `*<Op>Response` with parsed `JSON200`/`JSON400` fields. **Recommended.** |
| `gp.Client.*` | You want the raw `(*http.Response, error)` return — closest to the wire. |

Both expose one method per endpoint, named after the operation: `GetReportsMrr`, `PostDataCustomers`, `PutDataCustomersId`, `DeleteIntegrationsWebhooksId`, etc. The `WithResponses` variant appends `WithResponse` to each: `GetReportsMrrWithResponse`, etc.

Future versions will add ergonomic group fields (`gp.Reports.GetMrr(...)`, `gp.Data.Customers.Create(...)`) so callers don't need to spell out the full operation ID. The flat methods will keep working.

---

## Quick start

```go
package main

import (
    "context"
    "fmt"
    "log"
    "os"

    growpanel "github.com/growpanel/growpanel-sdk-go"
    gen "github.com/growpanel/growpanel-sdk-go/generated"
)

func ptr[T any](v T) *T { return &v }

func main() {
    gp, err := growpanel.New(os.Getenv("GROWPANEL_API_KEY"))
    if err != nil { log.Fatal(err) }
    ctx := context.Background()

    // Top-level KPIs
    summary, err := gp.WithResponses.GetReportsSummaryWithResponse(ctx, nil)
    if err != nil { log.Fatal(err) }
    fmt.Println("MRR (øre):", *summary.JSON200.Summary.MrrCurrent)

    // MRR time series with typed query params
    mrr, err := gp.WithResponses.GetReportsMrrWithResponse(ctx, &gen.GetReportsMrrParams{
        Date:     ptr("20260101-20260531"),
        Interval: ptr("month"),
    })
    if err != nil { log.Fatal(err) }
    for _, period := range mrr.JSON200.Result {
        fmt.Println(period.Date, period.TotalMrr)
    }
}
```

The generated types use pointers for optional fields, so a tiny helper makes literals cleaner:

```go
func ptr[T any](v T) *T { return &v }
```

---

## Usage by area

**Analytics — reports:**
```go
gp.WithResponses.GetReportsSummaryWithResponse(ctx, nil)
gp.WithResponses.GetReportsMrrWithResponse(ctx, &gen.GetReportsMrrParams{...})
gp.WithResponses.GetReportsMrrGrowthWithResponse(ctx, &gen.GetReportsMrrGrowthParams{...})
gp.WithResponses.GetReportsCohortWithResponse(ctx, &gen.GetReportsCohortParams{...})
gp.WithResponses.GetReportsRetentionWithResponse(ctx, &gen.GetReportsRetentionParams{...})
gp.WithResponses.GetReportsCashflowFailedPaymentsWithResponse(ctx, &gen.GetReportsCashflowFailedPaymentsParams{...})
// ... 22 report endpoints in total
```

**Customers (analytics view):**
```go
gp.WithResponses.GetCustomersWithResponse(ctx, &gen.GetCustomersParams{Limit: ptr("50")})
gp.WithResponses.GetCustomersIdWithResponse(ctx, "cus_xxx", nil)
```

**Plans (analytics view):**
```go
gp.WithResponses.GetPlansWithResponse(ctx)
```

**Account & integrations:**
```go
gp.WithResponses.GetProfileWithResponse(ctx)
gp.WithResponses.PutProfileWithResponse(ctx, gen.PutProfileJSONBody{FirstName: ptr("Lasse")})

gp.WithResponses.GetSettingsNotificationsWithResponse(ctx)
gp.WithResponses.GetIntegrationsWebhooksWithResponse(ctx)
gp.WithResponses.PostIntegrationsWebhooksWithResponse(ctx, gen.PostIntegrationsWebhooksJSONBody{
    Url:       "https://hooks.zapier.com/...",
    EventType: "movement.churn",
})
gp.WithResponses.DeleteIntegrationsWebhooksIdWithResponse(ctx, "wh_xxx")
```

**Data ingestion (every `/data/*` endpoint):**
```go
gp.WithResponses.GetDataCustomersWithResponse(ctx, &gen.GetDataCustomersParams{Source: "ds_xxx"})
gp.WithResponses.PostDataCustomersWithResponse(ctx, gen.PostDataCustomersJSONBody{...})
gp.WithResponses.PutDataCustomersIdWithResponse(ctx, "cus_xxx", gen.PutDataCustomersIdJSONBody{...})
gp.WithResponses.DeleteDataCustomersIdWithResponse(ctx, "cus_xxx")

gp.WithResponses.GetDataPlansWithResponse(ctx, &gen.GetDataPlansParams{Source: ptr("ds_xxx")})
gp.WithResponses.PostDataPlansWithResponse(ctx, gen.PostDataPlansJSONBody{...})

gp.WithResponses.GetDataInvoicesWithResponse(ctx, &gen.GetDataInvoicesParams{Source: "ds_xxx"})
gp.WithResponses.PostDataInvoicesWithResponse(ctx, gen.PostDataInvoicesJSONBody{...})

gp.WithResponses.GetDataPlanGroupsWithResponse(ctx, &gen.GetDataPlanGroupsParams{})
gp.WithResponses.PostDataPlanGroupsWithResponse(ctx, gen.PostDataPlanGroupsJSONBody{
    Name:  "Pro tier",
    Plans: []string{"price_a", "price_b"},
})

gp.WithResponses.GetDataSegmentsWithResponse(ctx)
gp.WithResponses.GetDataDataSourcesWithResponse(ctx, &gen.GetDataDataSourcesParams{})
gp.WithResponses.PostDataDataSourcesIdFullImportWithResponse(ctx, "ds_xxx")
gp.WithResponses.GetDataDataSourcesIdProgressWithResponse(ctx, "ds_xxx")
```

---

## Errors

The generated client returns `(*Response, error)`. Network/decoding errors land in `err`; API-level errors (4xx/5xx) come back on the response with status accessible via `resp.StatusCode()`.

```go
resp, err := gp.WithResponses.GetCustomersIdWithResponse(ctx, "cus_doesnotexist", nil)
if err != nil {
    return fmt.Errorf("network: %w", err)
}
switch resp.StatusCode() {
case 200:
    // resp.JSON200 has the typed payload
case 404:
    // customer not found
case 429:
    // rate-limited
}
```

See [error codes](/developers/rest-api/error-codes/) for what each status means.

---

## Updating

```bash
go get -u github.com/growpanel/growpanel-sdk-go
```

The SDK is regenerated on every API surface change. Changelog on [GitHub Releases](https://github.com/growpanel/growpanel-sdk-go/releases).

---

## Related

- [Interactive API reference](https://api.growpanel.io/docs)
- [JavaScript SDK](/developers/sdks/javascript/)
- [Python SDK](/developers/sdks/python/)
- [CLI](/developers/cli/)
- [MCP server](/developers/mcp-server/)

---

## Official SDKs
<!-- url: https://growpanel.io/developers/sdks/ -->

GrowPanel ships official SDKs in three languages. They're auto-generated from the same OpenAPI spec that powers the [interactive API reference](https://api.growpanel.io/docs), so they're always in lock-step with the API surface and stay typed end-to-end.

| Language | Install | Docs | Repo |
|----------|---------|------|------|
| **JavaScript / TypeScript** | `npm install @growpanel/sdk` | [Guide](/developers/sdks/javascript/) | [growpanel-sdk-js](https://github.com/growpanel/growpanel-sdk-js) |
| **Python** | `pip install growpanel` | [Guide](/developers/sdks/python/) | [growpanel-sdk-python](https://github.com/growpanel/growpanel-sdk-python) |
| **Go** | `go get github.com/growpanel/growpanel-sdk-go` | [Guide](/developers/sdks/go/) | [growpanel-sdk-go](https://github.com/growpanel/growpanel-sdk-go) |

---

## Why use an SDK?

You can talk to the REST API with any HTTP client (the [API reference](https://api.growpanel.io/docs) has working cURL examples for every endpoint). The SDKs add three things on top:

- **Typed parameters and responses.** Your editor autocompletes `gp.reports.getMrr({ query: { ... } })` and refuses to build if you misspell a field name.
- **One namespace per area.** Methods are grouped so you can explore the surface — `gp.reports.*`, `gp.customers.*`, `gp.data.*` for ingestion, `gp.webhooks.*` for event subscriptions.
- **Pagination, errors, and retries handled.** No fiddling with `Authorization: Bearer ...` headers or parsing `{ error: '...' }` envelopes — every method returns the typed payload or throws a `GrowPanelError`.

---

## Surface map (same across all SDKs)

The three SDKs expose the same shape, just in idiomatic naming for each language. Once you've used one, the others are familiar.

**Analytics (read-only):**
- `reports.*` — MRR, leads, cohorts, cashflow, retention, churn
- `customers.*` — list + detail (the analytics view)
- `plans.*` — list plans (analytics view)

**Account & integrations:**
- `profile.*` — current user
- `notifications.*` — report subscriptions + alert thresholds
- `webhooks.*` — event subscriptions (Zapier / n8n / Make)

**Ingestion (nested under `data.*`):**
- `data.customers.*` — CRUD raw customer rows
- `data.plans.*` — CRUD raw plans
- `data.planGroups.*` — group plans together
- `data.segments.*` — saved filter combinations
- `data.invoices.*` — CRUD raw invoices
- `data.sources.*` — connected billing systems + import controls

The `data.*` namespace mirrors the `/data/*` URL prefix — it's the "import API" for pushing your own data in, separate from the read-only analytics surfaces.

---

## How they're built

All three SDKs are auto-generated from `https://api.growpanel.io/openapi.json`:

- **JS/TS** — [`@hey-api/openapi-ts`](https://heyapi.dev/) generates the typed low-level client; a hand-written wrapper adds the namespaced ergonomics.
- **Python** — [`openapi-python-client`](https://github.com/openapi-generators/openapi-python-client) produces fully-typed attrs models + sync/async functions per operation.
- **Go** — [`oapi-codegen`](https://github.com/oapi-codegen/oapi-codegen) emits a struct-based client with one method per operation.

When the API surface changes, a GitHub Action regenerates and publishes all three SDKs along with the [CLI](/developers/cli/) and [MCP server](/developers/mcp-server/) — usually within minutes of a deploy.

---

## Related

- [Interactive API reference](https://api.growpanel.io/docs) — try-it-out + every endpoint
- [Authentication](/developers/rest-api/authentication/) — getting your API key
- [Rate limiting](/developers/rest-api/rate-limiting/) — request limits
- [Error codes](/developers/rest-api/error-codes/) — what each HTTP status means

---

## JavaScript / TypeScript SDK
<!-- url: https://growpanel.io/developers/sdks/javascript -->

Official TypeScript SDK for the GrowPanel REST API. Works in Node.js, Bun, Deno, Cloudflare Workers, and modern browsers. Published as `@growpanel/sdk` on npm. Source: [growpanel/growpanel-sdk-js](https://github.com/growpanel/growpanel-sdk-js).

---

## Installation

```bash
npm install @growpanel/sdk
```

Requires Node.js 20+ (or any runtime with `fetch` and ES modules). TypeScript types ship with the package — no `@types/...` needed.

---

## Authentication

Get an API key from **app.growpanel.io → Account → API keys**, then pass it when constructing the client. See [Authentication](/developers/rest-api/authentication/) for key scoping and rotation.

```typescript
import { GrowPanel } from '@growpanel/sdk';

const gp = new GrowPanel({
    apiKey: process.env.GROWPANEL_API_KEY!
});
```

You can also override the base URL (useful for testing against `api-dev.growpanel.io` or a self-hosted proxy):

```typescript
const gp = new GrowPanel({
    apiKey: process.env.GROWPANEL_API_KEY!,
    baseUrl: 'https://api-dev.growpanel.io'
});
```

---

## Quick start

```typescript
// Top-level KPIs
const summary = await gp.reports.getSummary();
console.log(summary.data.summary.mrr_current);

// MRR time series for a specific window
const mrr = await gp.reports.getMrr({
    query: { date: '20260101-20260531', interval: 'month' }
});
for (const period of mrr.data.result) {
    console.log(period.date, period.total_mrr);
}

// List customers
const customers = await gp.customers.list({ query: { limit: '50' } });
for (const c of customers.data.result.list) {
    console.log(c.name, c.current_mrr_base_currency);
}
```

---

## Surfaces

**Analytics:**
```typescript
gp.reports.getSummary()
gp.reports.getMrr({ query: { date, interval, breakdown } })
gp.reports.getCohort({ query: { date, interval } })
gp.reports.getRetention({ query: { date, interval } })
gp.reports.getCashflowFailedPayments({ query: { date } })
// ... 22 report endpoints in total

gp.customers.list({ query: { limit, search } })
gp.customers.detail({ path: { id } })
gp.plans.list()
```

**Account & integrations:**
```typescript
gp.profile.get()
gp.profile.update({ body: { first_name, timezone } })
gp.notifications.get()
gp.notifications.update({ body: { daily_report: true } })
gp.webhooks.list()
gp.webhooks.create({ body: { url, event_type } })
gp.webhooks.delete({ path: { id } })
```

**Ingestion (nested `data.*` namespace):**
```typescript
gp.data.customers.create({ body: { id, name, email, created_date, country, data_source } })
gp.data.customers.update({ path: { id }, body: { name } })
gp.data.customers.delete({ path: { id } })

gp.data.plans.create({ body: { id, name, billing_freq, currency, data_source } })
gp.data.invoices.create({ body: { id, customer_id, date, amount, currency, data_source } })

gp.data.planGroups.create({ body: { name, plans: ['price_a', 'price_b'] } })
gp.data.segments.create({ body: { name, filters: { region: 'eu' } } })

gp.data.sources.list()
gp.data.sources.fullImport({ path: { id } })
gp.data.sources.getProgress({ path: { id } })
```

Bulk-friendly endpoints (`data.customers.create`, `data.invoices.create`, `data.plans.create`) accept either a single object or an array — the response is always an array of per-row import results.

For anything not in a named namespace, `gp.raw.<operationId>(...)` exposes every generated operation by its OpenAPI ID.

---

## Errors

Every method throws `GrowPanelError` on non-2xx responses. Inspect `err.status`, `err.statusText`, and `err.body` for context.

```typescript
import { GrowPanel, GrowPanelError } from '@growpanel/sdk';

try {
    await gp.customers.detail({ path: { id: 'cus_doesnotexist' } });
} catch (err) {
    if (err instanceof GrowPanelError) {
        if (err.status === 404) {
            // customer doesn't exist
        } else if (err.status === 429) {
            // rate-limited — see Rate limiting docs
        }
    }
    throw err;
}
```

See [error codes](/developers/rest-api/error-codes/) for the full list.

---

## Custom fetch

The SDK uses the global `fetch` by default. Pass a custom implementation for retries, instrumentation, or Workers-specific bindings:

```typescript
const gp = new GrowPanel({
    apiKey: process.env.GROWPANEL_API_KEY!,
    fetch: env.MY_INSTRUMENTED_FETCH
});
```

---

## Updating

The SDK is regenerated on every API surface change — usually within minutes of a deploy. To pull the latest:

```bash
npm install @growpanel/sdk@latest
```

Changelog and breaking changes are on [GitHub Releases](https://github.com/growpanel/growpanel-sdk-js/releases).

---

## Related

- [Interactive API reference](https://api.growpanel.io/docs) — every endpoint with try-it-out
- [Python SDK](/developers/sdks/python/)
- [Go SDK](/developers/sdks/go/)
- [CLI](/developers/cli/) — same API surface, terminal-friendly
- [MCP server](/developers/mcp-server/) — same API surface, for AI assistants

---

## Python SDK
<!-- url: https://growpanel.io/developers/sdks/python -->

Official Python SDK for the GrowPanel REST API. Published as `growpanel` on PyPI. Source: [growpanel/growpanel-sdk-python](https://github.com/growpanel/growpanel-sdk-python).

---

## Installation

```bash
pip install growpanel
```

Requires Python 3.9+. Built on top of `httpx` and `attrs` — both are installed automatically.

---

## Authentication

Get an API key from **app.growpanel.io → Account → API keys**, then pass it as `api_key`. See [Authentication](/developers/rest-api/authentication/) for key scoping and rotation.

```python
import os
from growpanel import GrowPanel

gp = GrowPanel(api_key=os.environ["GROWPANEL_API_KEY"])
```

Override the base URL for staging:

```python
gp = GrowPanel(
    api_key=os.environ["GROWPANEL_API_KEY"],
    base_url="https://api-dev.growpanel.io"
)
```

The client supports the context-manager protocol so HTTP connections are cleaned up properly:

```python
with GrowPanel(api_key=...) as gp:
    summary = gp.reports.get_reports_summary()
```

---

## Naming convention

`v0.1` uses the full operation name from the OpenAPI spec for each method — so `GET /reports/summary` is reached via `gp.reports.get_reports_summary()`, not `gp.reports.summary()` or `gp.reports.get_summary()`. This keeps the SDK names stable and predictable as long as the spec's `operationId`s are stable, but it does mean the prefix repeats inside namespaces (e.g. `gp.reports.get_reports_mrr()`). A future v0.2 will add short aliases (`gp.reports.summary()` etc.) — the long forms will keep working.

---

## Quick start

```python
# Top-level KPIs
summary = gp.reports.get_reports_summary()
print(summary.summary.mrr_current)

# MRR time series
mrr = gp.reports.get_reports_mrr(date="20260101-20260531", interval="month")
for period in mrr.result:
    print(period.date, period.total_mrr)

# List customers
customers = gp.customers.get_customers(limit="50")
for c in customers.result.list_:
    print(c.name, c.mrr)
```

Note: `list` is a reserved word in Python, so openapi-python-client renames the response field to `list_` (trailing underscore). You'll see that in customers, plans, and any other list-shaped response.

---

## Surfaces

**Analytics — reports:**
```python
gp.reports.get_reports_summary()
gp.reports.get_reports_mrr(date="...", interval="...", breakdown="...")
gp.reports.get_reports_mrr_growth(date="...")
gp.reports.get_reports_cmrr_summary()
gp.reports.get_reports_leads(date="...", interval="...")
gp.reports.get_reports_leads_summary(date="...")
gp.reports.get_reports_cohort(date="...", interval="...")
gp.reports.get_reports_retention(date="...", interval="...")
gp.reports.get_reports_transactions(date="...", interval="...")
gp.reports.get_reports_transactions_summary(date="...")
gp.reports.get_reports_cashflow_failed_payments(date="...")
gp.reports.get_reports_cashflow_failed_payments_summary(date="...")
gp.reports.get_reports_cashflow_refunds(date="...")
gp.reports.get_reports_cashflow_failure_rate(date="...")
gp.reports.get_reports_cashflow_failure_rate_summary(date="...")
gp.reports.get_reports_cashflow_outstanding_unpaid()
gp.reports.get_reports_churn_scheduled(date="...")
gp.reports.get_reports_churn_reasons_summary(date="...")
gp.reports.get_reports_map()
gp.reports.get_reports_latest_activity(limit="10")
gp.reports.get_reports_customer_concentration()
gp.reports.get_reports_custom_variables(key="industry")
```

**Customers (analytics view):**
```python
gp.customers.get_customers(limit="50", search="acme")
gp.customers.get_customers_id(id="cus_xxx")
```

**Plans (analytics view):**
```python
gp.plans.get_plans()
```

**Account & integrations:**
```python
gp.profile.get_profile()
gp.profile.put_profile(body={"first_name": "Lasse", "timezone": "Europe/Copenhagen"})

gp.notifications.get_settings_notifications()
gp.notifications.put_settings_notifications(body={"daily_report": True})

gp.webhooks.get_integrations_webhooks()
gp.webhooks.post_integrations_webhooks(body={"url": "https://hooks.zapier.com/...", "event_type": "movement.churn"})
gp.webhooks.delete_integrations_webhooks_id(id="wh_xxx")
```

**Data ingestion (`gp.data.*`):**
```python
gp.data.customers.get_data_customers(source="ds_xxx", limit="50")
gp.data.customers.get_data_customers_id(id="cus_xxx", source="ds_xxx")
gp.data.customers.post_data_customers(body={
    "id": "cus_xxx",
    "name": "Acme Inc",
    "email": "billing@acme.com",
    "created_date": "2025-01-15",
    "country": "us",
    "data_source": "ds_xxx"
})
gp.data.customers.put_data_customers_id(id="cus_xxx", body={"name": "Acme Corporation"})
gp.data.customers.delete_data_customers_id(id="cus_xxx")

gp.data.plans.get_data_plans(source="ds_xxx")
gp.data.plans.post_data_plans(body={
    "id": "price_pro",
    "name": "Pro",
    "billing_freq": "month",
    "currency": "usd",
    "data_source": "ds_xxx"
})

gp.data.invoices.get_data_invoices(source="ds_xxx")
gp.data.invoices.post_data_invoices(body={
    "id": "in_xxx", "customer_id": "cus_xxx",
    "date": "2025-01-15", "amount": 9900,
    "currency": "usd", "data_source": "ds_xxx"
})

gp.data.plan_groups.get_data_plan_groups()
gp.data.plan_groups.post_data_plan_groups(body={"name": "Pro tier", "plans": ["price_a", "price_b"]})

gp.data.segments.get_data_segments()
gp.data.segments.post_data_segments(body={"name": "EU Enterprise", "filters": {"region": "eu"}})

gp.data.sources.get_data_data_sources()
gp.data.sources.post_data_data_sources(body={"name": "My Stripe", "type": "stripe"})
gp.data.sources.post_data_data_sources_id_full_import(id="ds_xxx")
gp.data.sources.get_data_data_sources_id_progress(id="ds_xxx")
```

Bulk-friendly endpoints (`post_data_customers`, `post_data_invoices`, `post_data_plans`) accept either a dict or a list of dicts — the response is always a list of per-row import results.

For anything not in a named namespace, `gp.raw` is the generated module tree — every endpoint is reachable from there.

---

## Errors

Every operation raises `GrowPanelError` on non-2xx responses.

```python
from growpanel import GrowPanel, GrowPanelError

try:
    gp.customers.get_customers_id(id="cus_doesnotexist")
except GrowPanelError as err:
    if err.status == 404:
        ...   # customer doesn't exist
    elif err.status == 429:
        ...   # rate-limited
    raise
```

`err.status`, `err.status_text`, and `err.body` carry the full context. See [error codes](/developers/rest-api/error-codes/) for what each status means.

---

## Updating

The SDK is regenerated on every API surface change. To pull the latest:

```bash
pip install --upgrade growpanel
```

Changelog on [GitHub Releases](https://github.com/growpanel/growpanel-sdk-python/releases).

---

## Related

- [Interactive API reference](https://api.growpanel.io/docs)
- [JavaScript SDK](/developers/sdks/javascript/)
- [Go SDK](/developers/sdks/go/)
- [CLI](/developers/cli/)
- [MCP server](/developers/mcp-server/)

---
