Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions docs/api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# SubTrackr API Documentation

**Current stable version: v1**

---

## Quick links

| Resource | Description |
|----------|-------------|
| [OpenAPI Spec](./openapi.yaml) | Machine-readable OpenAPI 3.0.3 specification |
| [Interactive Explorer](./swagger.html) | Swagger UI — try every endpoint in-browser |
| [Webhook Reference](./webhooks.md) | All event types, payloads, and retry policy |
| [JS/TS SDK](./sdk/javascript.md) | JavaScript and TypeScript examples |
| [Python SDK](./sdk/python.md) | Python examples |
| [Go SDK](./sdk/go.md) | Go examples |
| [Getting Started](./guides/getting-started.md) | First integration in 7 steps |
| [SaaS Integration](./guides/saas-integration.md) | Feature gating, dunning, upgrade flows |
| [Theme Integration](./guides/theme-integration.md) | White-label brand theming |

---

## API versioning

| Version | Status | End-of-life |
|---------|--------|-------------|
| `v1` | **Stable** — current | — |
| `v0` | Deprecated | 2025-06-01 |

Breaking changes are introduced under a new major version with a minimum
6-month deprecation window. Non-breaking additions (new optional fields, new
endpoints) may be added to `v1` at any time.

Specify the version in the path:

```
https://api.subtrackr.io/v1/subscriptions
```

---

## Authentication

All endpoints require a Bearer token:

```
Authorization: Bearer <your-api-key>
```

API keys are scoped to a merchant and can be rotated from **Settings → API Keys**.

---

## Rate limits

| Scope | Limit |
|-------|-------|
| Default per key | 60 requests / minute |
| Burst | 10 requests / second |
| Webhook delivery | 3 concurrent per endpoint |

Rate-limit headers are returned on every response:
- `X-RateLimit-Limit`
- `X-RateLimit-Remaining`
- `X-RateLimit-Reset` (Unix timestamp)

---

## Environments

| Environment | Base URL | Purpose |
|-------------|----------|---------|
| Production | `https://api.subtrackr.io/v1` | Live traffic |
| Sandbox | `https://sandbox.subtrackr.io/v1` | Testing — no real charges |

Sandbox API keys are prefixed with `sk_test_`.

---

## Support

- API issues: [GitHub Issues](https://github.com/Smartdevs17/SubTrackr/issues)
- Email: api@subtrackr.io
132 changes: 132 additions & 0 deletions docs/api/guides/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Getting Started

This guide walks you through your first integration with the SubTrackr API —
from creating a customer to processing a subscription payment.

## Prerequisites

- A SubTrackr account (sign up at [app.subtrackr.io](https://app.subtrackr.io))
- An API key from **Settings → API Keys**
- Node.js 18+ (or Python 3.10+, or Go 1.21+)

---

## Step 1 — Install the SDK

```bash
npm install @subtrackr/sdk
```

## Step 2 — Initialise the client

```typescript
import { SubTrackr } from '@subtrackr/sdk';

const client = new SubTrackr({
apiKey: process.env.SUBTRACKR_API_KEY!,
baseUrl: 'https://sandbox.subtrackr.io/v1', // use sandbox first
});
```

## Step 3 — Create a plan

```typescript
const plan = await client.plans.create({
name: 'Pro Monthly',
price: 29.99,
currency: 'USD',
billingCycle: 'monthly',
trialDays: 14,
features: ['Unlimited projects', 'Priority support'],
});

console.log('Plan created:', plan.id); // plan_monthly_pro
```

## Step 4 — Create a customer

```typescript
const customer = await client.customers.create({
email: 'jane@example.com',
name: 'Jane Doe',
});

console.log('Customer created:', customer.id); // cus_xyz789
```

## Step 5 — Subscribe the customer

```typescript
const subscription = await client.subscriptions.create({
customerId: customer.id,
planId: plan.id,
trialEnd: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000),
});

console.log('Subscription created:', subscription.id);
console.log('Status:', subscription.status); // trialing
```

## Step 6 — Register a webhook

```typescript
const endpoint = await client.webhooks.create({
url: 'https://your-app.com/webhooks/subtrackr',
events: [
'subscription.created',
'subscription.trial_will_end',
'invoice.paid',
'invoice.payment_failed',
],
});

// Store this securely — never log or expose it
const signingSecret = endpoint.signingSecret;
```

## Step 7 — Handle webhook events

```typescript
import express from 'express';
import { verifyWebhookSignature } from '@subtrackr/sdk';

const app = express();

app.post(
'/webhooks/subtrackr',
express.raw({ type: 'application/json' }),
(req, res) => {
const sig = req.headers['subtrackr-signature'] as string;
const event = verifyWebhookSignature(req.body, sig, signingSecret);

if (!event) return res.status(400).send('Invalid signature');

switch (event.type) {
case 'subscription.trial_will_end':
// Send reminder email to customer
sendTrialEndingEmail(event.data.id);
break;
case 'invoice.paid':
// Unlock features for paid tier
grantAccess(event.data.customerId);
break;
case 'invoice.payment_failed':
// Notify customer to update payment method
sendPaymentFailedEmail(event.data.customerId);
break;
}

res.json({ received: true });
}
);

app.listen(3000);
```

---

## What's next?

- [Webhook Event Reference](../webhooks.md) — full event catalogue
- [White-label Themes](./theme-integration.md) — customise the UI for your brand
- [SaaS Integration Guide](./saas-integration.md) — end-to-end SaaS pattern
169 changes: 169 additions & 0 deletions docs/api/guides/saas-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# SaaS Integration Guide

A complete end-to-end pattern for integrating SubTrackr into a SaaS application:
feature gating, upgrade/downgrade flows, and dunning management.

---

## Architecture overview

```
User signs up
Create Customer (SubTrackr)
User selects plan → Create Subscription
├─ status: trialing ──► trial_will_end webhook ──► prompt payment method
└─ status: active
├─ invoice.paid ──► grant / maintain access
├─ invoice.payment_failed ──► past_due ──► dunning emails
├─ subscription.cancelled ──► revoke access at period end
└─ subscription.updated ──► sync plan to feature flags
```

---

## Feature gating

Map each plan to a set of feature flags and check them on every protected
route:

```typescript
// plans.config.ts
export const PLAN_FEATURES: Record<string, string[]> = {
plan_free: ['basic_analytics'],
plan_monthly_pro: ['basic_analytics', 'advanced_analytics', 'api_access'],
plan_enterprise: ['basic_analytics', 'advanced_analytics', 'api_access', 'sso', 'audit_logs'],
};

// middleware/requireFeature.ts
import { SubTrackr } from '@subtrackr/sdk';
import { PLAN_FEATURES } from './plans.config';

export function requireFeature(feature: string) {
return async (req: Request, res: Response, next: NextFunction) => {
const sub = await getActiveSubscription(req.user.id); // your DB lookup
const features = PLAN_FEATURES[sub?.planId ?? 'plan_free'] ?? [];

if (!features.includes(feature)) {
return res.status(403).json({ error: 'upgrade_required', feature });
}
next();
};
}

// Usage
app.get('/api/analytics/advanced', requireFeature('advanced_analytics'), handler);
```

---

## Upgrade / downgrade flow

```typescript
// Upgrade: switch plan immediately
async function upgradePlan(subscriptionId: string, newPlanId: string) {
const updated = await client.subscriptions.update(subscriptionId, {
planId: newPlanId,
});
// Webhook subscription.updated fires — sync feature flags
return updated;
}

// Downgrade: switch at period end to avoid surprise charges
async function downgradePlan(subscriptionId: string, newPlanId: string) {
const updated = await client.subscriptions.update(subscriptionId, {
planId: newPlanId,
// apply at next billing cycle
prorationBehavior: 'none',
});
return updated;
}
```

---

## Dunning management

When `invoice.payment_failed` fires, kick off a dunning sequence:

```typescript
const DUNNING_SEQUENCE = [
{ delayDays: 0, message: 'Your payment failed. Please update your card.' },
{ delayDays: 3, message: 'Reminder: your account will be suspended in 4 days.' },
{ delayDays: 7, message: 'Final notice: update payment to avoid cancellation.' },
];

async function startDunning(customerId: string, invoiceId: string) {
for (const step of DUNNING_SEQUENCE) {
await scheduleEmail({
to: customer.email,
sendAt: addDays(new Date(), step.delayDays),
body: step.message,
meta: { invoiceId },
});
}
}

// In your webhook handler
case 'invoice.payment_failed':
await startDunning(event.data.customerId, event.data.id);
break;

// Cancel after final dunning failure
case 'subscription.expired':
await revokeAllAccess(event.data.customerId);
break;
```

---

## Idempotent webhook processing

Always use the event `id` as a deduplication key:

```typescript
import { db } from './database';

async function processWebhookEvent(event: WebhookEvent) {
const existing = await db.webhookEvents.findUnique({ where: { id: event.id } });
if (existing) return; // already processed

await db.webhookEvents.create({ data: { id: event.id, type: event.type } });

// Now safe to process
switch (event.type) {
// ...
}
}
```

---

## White-label theme per merchant

```typescript
// On merchant onboarding
const theme = await client.themes.create({
id: `brand-${merchant.slug}`,
name: merchant.name,
mode: 'dark',
colors: {
primary: merchant.brandColor,
secondary: darken(merchant.brandColor, 20),
accent: merchant.accentColor,
// ... other colors
},
logoUri: merchant.logoUrl,
});

// Store theme.id in your merchant record
await db.merchants.update({ where: { id: merchant.id }, data: { themeId: theme.id } });
```

See [Theme Integration Guide](./theme-integration.md) for the full white-label setup.
Loading