LetsBeBiz-Redesign/docs/technical/LetsBe_Biz_API_Reference.md

691 lines
27 KiB
Markdown

# LetsBe Biz — API Reference
**Version:** 1.0
**Date:** February 26, 2026
**Authors:** Matt (Founder), Claude (Architecture)
**Status:** Engineering Spec — Ready for Implementation
**Companion docs:** Technical Architecture v1.2, Repo Analysis v1.0, Infrastructure Runbook v1.0
---
## 1. Purpose
This document is the complete API reference for the LetsBe Biz platform. It covers:
1. **Hub API** — The central platform's REST endpoints (admin, customer, tenant, webhooks)
2. **Safety Wrapper API** — The on-tenant endpoints for Hub ↔ VPS communication
3. **Authentication flows** — How each actor authenticates
4. **Webhook specifications** — Inbound and outbound webhook contracts
This reference is designed to serve as the implementation blueprint for Claude Code and Codex sessions.
---
## 2. API Overview
### 2.1 Base URLs
| API | Base URL | Auth |
|-----|----------|------|
| Hub (admin) | `https://hub.letsbe.biz/api/v1/admin/` | Bearer {staffSessionToken} |
| Hub (customer) | `https://hub.letsbe.biz/api/v1/customer/` | Bearer {customerSessionToken} |
| Hub (tenant comms) | `https://hub.letsbe.biz/api/v1/tenant/` | Bearer {hubApiKey} |
| Hub (public) | `https://hub.letsbe.biz/api/v1/public/` | None or API key |
| Hub (webhooks) | `https://hub.letsbe.biz/api/v1/webhooks/` | Signature verification |
| Safety Wrapper (local) | `http://127.0.0.1:8100/` | Internal only |
| OpenClaw Gateway (local) | `http://127.0.0.1:18789/` | Token auth |
### 2.2 Common Patterns
**Response format:** All Hub API responses follow this envelope:
```json
{
"success": true,
"data": { ... },
"meta": {
"page": 1,
"pageSize": 25,
"total": 142
}
}
```
**Error format:**
```json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid email format",
"details": [
{ "field": "email", "message": "Must be a valid email address" }
]
}
}
```
**Pagination:** Cursor-based where possible, offset-based for admin lists.
- `?page=1&pageSize=25` (offset)
- `?cursor=abc123&limit=25` (cursor)
**Validation:** Zod schemas on all endpoints. 400 returned with field-level errors.
**Rate limiting:** 60 requests/minute per authenticated session. 429 returned with `Retry-After` header.
---
## 3. Authentication
### 3.1 Staff Authentication (Admin Panel)
NextAuth.js with Credentials provider, JWT sessions.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET/POST | `/api/auth/[...nextauth]` | None | NextAuth handlers (login, logout, session) |
| POST | `/api/v1/setup` | None (one-time) | Initial owner account creation |
**Login flow:**
```
1. POST /api/auth/callback/credentials
Body: { email, password }
2. If 2FA enabled:
Response: { requires2FA: true, pendingToken: "..." }
3. POST /api/v1/auth/2fa/verify
Body: { token: pendingToken, code: "123456" }
Response: Sets session cookie
4. Session token in cookie → used as Bearer token for API calls
```
**2FA Management:**
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/api/v1/auth/2fa/status` | Session | Check if 2FA is enabled |
| POST | `/api/v1/auth/2fa/setup` | Session | Generate TOTP secret + QR code |
| POST | `/api/v1/auth/2fa/verify` | Session/Pending | Verify TOTP code |
| POST | `/api/v1/auth/2fa/disable` | Session | Disable 2FA (requires current code) |
| GET | `/api/v1/auth/2fa/backup-codes` | Session | Get/regenerate backup codes |
**Staff Invitations:**
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/api/v1/auth/invite/{token}` | None | Look up invitation details |
| POST | `/api/v1/auth/accept-invite` | None | Accept invitation, set password |
### 3.2 Customer Authentication (Customer Portal)
Same NextAuth.js flow, separate user type. Customers log in via:
- Email + password (created during Stripe checkout)
- Future: SSO via Keycloak on their tenant VPS
### 3.3 Tenant Authentication (Safety Wrapper ↔ Hub)
Bearer token authentication using a Hub-generated API key.
**Registration flow:**
```
1. During provisioning, the Hub generates a registration token per order
2. Safety Wrapper boots, calls:
POST /api/v1/tenant/register
Body: { registrationToken: "..." }
3. Hub validates token, returns:
{ hubApiKey: "hk_abc123..." }
4. Safety Wrapper stores hubApiKey in encrypted secrets registry
5. All subsequent requests use:
Authorization: Bearer hk_abc123...
```
### 3.4 Stripe Webhook Authentication
Stripe webhook signature verification using `stripe.webhooks.constructEvent()`.
---
## 4. Hub Admin API (Staff)
### 4.1 Profile
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/profile` | Get current staff profile |
| PATCH | `/api/v1/profile` | Update name, email |
| POST | `/api/v1/profile/photo` | Upload profile photo (multipart/form-data → S3/MinIO) |
| POST | `/api/v1/profile/password` | Change password (requires current password) |
### 4.2 Customers
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/customers` | List customers. Query: `?page=1&pageSize=25&search=acme` |
| POST | `/api/v1/admin/customers` | Create customer (staff-initiated) |
| GET | `/api/v1/admin/customers/{id}` | Get customer detail with orders, subscription |
| PATCH | `/api/v1/admin/customers/{id}` | Update customer details |
**Customer object:**
```json
{
"id": "cust_abc123",
"email": "maria@acme.com",
"name": "Maria Weber",
"company": "Acme Marketing GmbH",
"status": "ACTIVE",
"subscription": {
"plan": "PRO",
"tier": "Build",
"tokenLimit": 15000000,
"tokensUsed": 8234000,
"stripeCustomerId": "cus_xxx",
"status": "ACTIVE"
},
"orders": [ ... ],
"foundingMember": {
"number": 42,
"tokenMultiplier": 2,
"expiresAt": "2027-03-15T00:00:00Z"
},
"createdAt": "2026-03-15T10:00:00Z"
}
```
### 4.3 Orders
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/orders` | List orders. Query: `?status=FULFILLED&tier=Build&search=acme` |
| POST | `/api/v1/admin/orders` | Create order (staff-initiated, MANUAL mode) |
| GET | `/api/v1/admin/orders/{id}` | Get order detail (full — server IP, tools, domain, status) |
| PATCH | `/api/v1/admin/orders/{id}` | Update order (credentials, server details) |
**Order lifecycle:**
| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/v1/admin/orders/{id}/provision` | Spawn Docker provisioner container |
| GET | `/api/v1/admin/orders/{id}/logs/stream` | SSE stream of provisioning logs |
| GET | `/api/v1/admin/orders/{id}/dns` | Get DNS verification status |
| POST | `/api/v1/admin/orders/{id}/dns/verify` | Trigger DNS A-record verification |
| POST | `/api/v1/admin/orders/{id}/dns/skip` | Manual DNS override |
| POST | `/api/v1/admin/orders/{id}/dns/create` | **NEW:** Auto-create DNS A records (Cloudflare/Entri) |
| GET | `/api/v1/admin/orders/{id}/automation` | Get automation mode (AUTO/MANUAL/PAUSED) |
| PATCH | `/api/v1/admin/orders/{id}/automation` | Change automation mode |
**Order object (key fields):**
```json
{
"id": "ord_abc123",
"userId": "cust_abc123",
"status": "FULFILLED",
"tier": "Build",
"domain": "acme.letsbe.biz",
"tools": ["nextcloud", "chatwoot", "ghost", "calcom", "odoo", "stalwart", "listmonk", "umami"],
"serverIp": "88.99.xx.xx",
"sshPort": 22022,
"automationMode": "AUTO",
"dashboardUrl": "https://acme.letsbe.biz",
"portainerUrl": "https://portainer.acme.letsbe.biz",
"createdAt": "2026-03-15T10:00:00Z",
"fulfilledAt": "2026-03-15T12:34:56Z"
}
```
### 4.4 Container Management (via Portainer)
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/orders/{id}/containers` | List all containers on the tenant VPS |
| GET | `/api/v1/admin/orders/{id}/containers/stats` | All container stats (CPU, RAM) |
| GET | `/api/v1/admin/orders/{id}/containers/{cId}` | Container detail |
| POST | `/api/v1/admin/orders/{id}/containers/{cId}/start` | Start container |
| POST | `/api/v1/admin/orders/{id}/containers/{cId}/stop` | Stop container |
| POST | `/api/v1/admin/orders/{id}/containers/{cId}/restart` | Restart container |
| GET | `/api/v1/admin/orders/{id}/containers/{cId}/logs` | Container logs. Query: `?tail=100&since=1h` |
| GET | `/api/v1/admin/orders/{id}/containers/{cId}/stats` | Container resource stats |
| GET | `/api/v1/admin/orders/{id}/portainer` | Get Portainer credentials |
| POST | `/api/v1/admin/orders/{id}/portainer/init` | Initialize Portainer endpoint |
| POST | `/api/v1/admin/orders/{id}/test-ssh` | Test SSH connectivity |
### 4.5 Servers
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/servers` | List active servers (from fulfilled orders) |
| GET | `/api/v1/admin/servers/{id}/health` | Server health (heartbeat status, Safety Wrapper version) |
| POST | `/api/v1/admin/servers/{id}/command` | Queue remote command for Safety Wrapper |
| POST | `/api/v1/admin/portainer/ping` | Test Portainer connectivity |
### 4.6 Netcup Integration
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/netcup/auth` | Get Netcup OAuth2 connection status |
| POST | `/api/v1/admin/netcup/auth` | Initiate OAuth2 Device Flow |
| DELETE | `/api/v1/admin/netcup/auth` | Disconnect Netcup |
| GET | `/api/v1/admin/netcup/servers` | List Netcup servers |
| GET | `/api/v1/admin/netcup/servers/{id}` | Server detail (IP, status, plan) |
| PATCH | `/api/v1/admin/netcup/servers/{id}` | Power action (start/stop/restart), hostname, nickname |
| GET | `/api/v1/admin/netcup/servers/{id}/metrics` | CPU, disk, network metrics |
| GET | `/api/v1/admin/netcup/servers/{id}/snapshots` | List snapshots |
| POST | `/api/v1/admin/netcup/servers/{id}/snapshots` | Create snapshot |
| DELETE | `/api/v1/admin/netcup/servers/{id}/snapshots/{snapId}` | Delete snapshot |
| POST | `/api/v1/admin/netcup/servers/{id}/snapshots/{snapId}/revert` | Revert to snapshot |
### 4.7 Staff Management
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/staff` | List staff members |
| POST | `/api/v1/admin/staff/invite` | Send staff invitation email |
| GET | `/api/v1/admin/staff/invitations` | List pending invitations |
| PATCH | `/api/v1/admin/staff/{id}` | Update staff role/status |
| DELETE | `/api/v1/admin/staff/{id}` | Deactivate staff member |
**Roles and permissions:**
| Role | Level | Key Permissions |
|------|-------|-----------------|
| OWNER | Full | All permissions, manage staff, delete account |
| ADMIN | High | Manage customers, orders, servers, billing |
| MANAGER | Medium | View/manage customers and orders, no billing |
| SUPPORT | Low | View customers, view orders, manage containers |
### 4.8 Agent Management (NEW)
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/agents/templates` | List available agent role templates |
| POST | `/api/v1/admin/orders/{id}/agents` | Deploy new agent to tenant server |
| GET | `/api/v1/admin/orders/{id}/agents` | List agents on tenant server |
| PATCH | `/api/v1/admin/orders/{id}/agents/{agentId}` | Update agent config (SOUL.md, tools, autonomy) |
| DELETE | `/api/v1/admin/orders/{id}/agents/{agentId}` | Remove agent from tenant |
**AgentConfig object:**
```json
{
"id": "agcfg_abc123",
"orderId": "ord_abc123",
"agentId": "it-admin",
"name": "IT Admin",
"role": "it-admin",
"soulMd": "# IT Admin\n\n## Identity\n...",
"toolsAllowed": ["shell", "docker", "file_read", "file_write", "env_read", "env_update"],
"toolsDenied": [],
"toolProfile": "coding",
"autonomyLevel": 3,
"externalCommsUnlocks": null,
"isActive": true
}
```
### 4.9 Command Approval Queue (NEW)
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/approvals` | List all pending approvals across tenants |
| GET | `/api/v1/admin/approvals/{id}` | Approval detail with full command context |
| POST | `/api/v1/admin/approvals/{id}` | Approve or deny. Body: `{ "action": "approve" \| "deny", "reason": "..." }` |
**CommandApproval object:**
```json
{
"id": "appr_abc123",
"orderId": "ord_abc123",
"agentId": "it-admin",
"commandClass": "red",
"toolName": "file_delete",
"toolArgs": { "path": "/opt/letsbe/stacks/nextcloud/data/tmp/*", "recursive": true },
"humanReadable": "IT Admin wants to delete /nextcloud/data/tmp/* (47 files, 2.3GB)",
"status": "PENDING",
"requestedAt": "2026-03-15T14:22:00Z",
"expiresAt": "2026-03-16T14:22:00Z"
}
```
### 4.10 Billing & Token Metering (NEW)
| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/v1/admin/billing/usage` | Ingest token usage report from Safety Wrapper |
| GET | `/api/v1/admin/billing/{customerId}` | Customer billing summary |
| GET | `/api/v1/admin/billing/{customerId}/history` | Historical usage data |
| POST | `/api/v1/admin/billing/overages` | Trigger overage billing via Stripe |
**TokenUsageBucket object (reported by Safety Wrapper):**
```json
{
"agentId": "marketing",
"model": "deepseek/deepseek-v3.2",
"bucketHour": "2026-03-15T14:00:00Z",
"tokensInput": 45000,
"tokensOutput": 12000,
"tokensCacheRead": 28000,
"tokensCacheWrite": 0,
"webSearchCount": 2,
"webFetchCount": 1,
"costCents": 3
}
```
### 4.11 Analytics
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/analytics/tokens` | Global token usage overview (all tenants) |
| GET | `/api/v1/admin/analytics/tokens/{orderId}` | Per-tenant token usage |
| GET | `/api/v1/admin/analytics/costs` | Cost breakdown by customer/model/agent |
| GET | `/api/v1/admin/stats` | Platform statistics (total customers, orders, revenue) |
| GET | `/api/v1/admin/analytics` | Dashboard analytics (growth, churn, usage trends) |
### 4.12 Settings
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/settings` | Get all settings by category |
| PATCH | `/api/v1/admin/settings` | Update settings (bulk). Body: `{ "category.key": "value" }` |
| POST | `/api/v1/admin/settings/test-email` | Send test email |
| POST | `/api/v1/admin/settings/test-storage` | Test S3/MinIO connection |
**Settings categories:** docker, dockerhub, gitea, hub, provisioning, netcup, email, notifications, storage (50+ settings total).
### 4.13 Enterprise Client Management
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/enterprise-clients` | List enterprise clients |
| POST | `/api/v1/admin/enterprise-clients` | Create enterprise client |
| GET | `/api/v1/admin/enterprise-clients/{id}` | Client detail with servers |
| PATCH | `/api/v1/admin/enterprise-clients/{id}` | Update client |
| DELETE | `/api/v1/admin/enterprise-clients/{id}` | Deactivate client |
| GET | `/api/v1/admin/enterprise-clients/{id}/servers` | List client's servers |
| POST | `/api/v1/admin/enterprise-clients/{id}/servers` | Add server to client |
| GET | `/api/v1/admin/enterprise-clients/{id}/servers/{sId}/stats` | Server stats |
| GET | `/api/v1/admin/enterprise-clients/{id}/servers/{sId}/events` | Container events |
| GET | `/api/v1/admin/enterprise-clients/{id}/error-rules` | Error detection rules |
| POST | `/api/v1/admin/enterprise-clients/{id}/error-rules` | Create error rule |
| PATCH | `/api/v1/admin/enterprise-clients/{id}/error-rules/{rId}` | Update error rule |
| DELETE | `/api/v1/admin/enterprise-clients/{id}/error-rules/{rId}` | Delete error rule |
| GET | `/api/v1/admin/enterprise-clients/{id}/errors` | Detected errors |
| POST | `/api/v1/admin/enterprise-clients/{id}/errors/{eId}/acknowledge` | Acknowledge error |
| GET | `/api/v1/admin/enterprise-clients/{id}/notifications` | Notification settings |
| PATCH | `/api/v1/admin/enterprise-clients/{id}/notifications` | Update notification settings |
| POST | `/api/v1/admin/enterprise-clients/{id}/security/verify` | Request security verification code |
| POST | `/api/v1/admin/enterprise-clients/{id}/security/confirm` | Confirm verification code |
---
## 5. Hub Customer API (NEW)
Self-service portal for customers. All endpoints require customer session authentication.
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/customer/dashboard` | Customer overview (server status, agent status, recent activity) |
| GET | `/api/v1/customer/agents` | List customer's agents with status |
| GET | `/api/v1/customer/agents/{id}` | Get agent detail (SOUL.md, tools, autonomy) |
| PATCH | `/api/v1/customer/agents/{id}` | Update agent config (personality, tools, autonomy level) |
| PATCH | `/api/v1/customer/agents/{id}/external-comms` | Update external comms gate per tool |
| GET | `/api/v1/customer/agents/{id}/activity` | Agent activity feed |
| GET | `/api/v1/customer/agents/{id}/conversations` | Conversation history |
| GET | `/api/v1/customer/usage` | Token usage summary (per agent, per model, daily/weekly/monthly) |
| GET | `/api/v1/customer/usage/breakdown` | Detailed usage breakdown for billing transparency |
| GET | `/api/v1/customer/approvals` | Pending command approval queue |
| POST | `/api/v1/customer/approvals/{id}` | Approve or deny a pending command |
| GET | `/api/v1/customer/tools` | List deployed tools with status |
| GET | `/api/v1/customer/billing` | Current billing period, usage, overages |
| GET | `/api/v1/customer/billing/history` | Billing history |
| GET | `/api/v1/customer/backups` | Backup status and history |
| PATCH | `/api/v1/customer/settings` | Update customer preferences (timezone, locale, notifications) |
---
## 6. Hub Tenant Communication API (NEW)
Endpoints called by the Safety Wrapper on each tenant VPS. All require Bearer {hubApiKey} authentication.
| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/v1/tenant/register` | Safety Wrapper registers post-deploy. Body: `{ registrationToken }`. Returns: `{ hubApiKey }` |
| POST | `/api/v1/tenant/heartbeat` | Status heartbeat. Sent every 5 minutes. |
| GET | `/api/v1/tenant/config` | Pull full config (agent configs, autonomy levels, model routing) |
| POST | `/api/v1/tenant/approval-request` | Push command approval request to Hub |
| GET | `/api/v1/tenant/approval-response/{id}` | Poll for approval/denial (or via webhook) |
| POST | `/api/v1/tenant/usage` | Report token usage buckets (hourly) |
| POST | `/api/v1/tenant/backup-status` | Report backup execution status |
**Heartbeat request body:**
```json
{
"status": "healthy",
"openclawVersion": "v2026.2.1",
"safetyWrapperVersion": "v1.0.3",
"configVersion": 7,
"agents": {
"dispatcher": { "status": "active", "lastActiveAt": "2026-03-15T14:20:00Z" },
"it-admin": { "status": "active", "lastActiveAt": "2026-03-15T14:18:00Z" },
"marketing": { "status": "idle", "lastActiveAt": "2026-03-14T09:00:00Z" },
"secretary": { "status": "active", "lastActiveAt": "2026-03-15T14:22:00Z" },
"sales": { "status": "idle", "lastActiveAt": "2026-03-13T16:00:00Z" }
},
"resources": {
"cpuPercent": 23,
"memoryUsedMb": 6144,
"memoryTotalMb": 16384,
"diskUsedGb": 45,
"diskTotalGb": 320,
"containersRunning": 18,
"containersStopped": 2
},
"tokenUsage": {
"periodStart": "2026-03-01T00:00:00Z",
"tokensUsed": 8234000,
"tokenAllotment": 15000000,
"premiumTokensUsed": 125000,
"premiumCostCents": 1250
},
"backupStatus": {
"lastBackup": "2026-03-15T02:15:00Z",
"status": "success",
"sizeGb": 4.2
}
}
```
**Heartbeat response body:**
```json
{
"configVersion": 8,
"configChanged": true,
"pendingCommands": [
{ "id": "cmd_123", "type": "RESTART_SERVICE", "payload": { "service": "ghost" } }
],
"pendingApprovals": [
{ "id": "appr_456", "action": "approve" }
]
}
```
If `configChanged: true`, the Safety Wrapper follows up with `GET /api/v1/tenant/config` to pull the updated configuration.
---
## 7. Public API
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/api/v1/public/orders` | API key | Create order from external source (Stripe checkout redirect) |
| GET | `/api/v1/public/orders/{id}` | API key | Get order status (for progress page) |
| GET | `/api/v1/public/founding-members/count` | None | Get founding member spots remaining |
---
## 8. Webhooks
### 8.1 Inbound Webhooks (Hub Receives)
**Stripe Checkout:**
| Method | Path | Source | Description |
|--------|------|--------|-------------|
| POST | `/api/v1/webhooks/stripe` | Stripe | `checkout.session.completed` → Creates User + Subscription + Order |
**Stripe webhook payload handling:**
```
Event: checkout.session.completed
→ Verify signature (stripe.webhooks.constructEvent)
→ Extract: customer email, plan, payment amount
→ Create User (status: ACTIVE)
→ Create Subscription (plan mapping: STARTER/PRO/ENTERPRISE)
→ Create Order (status: PAYMENT_CONFIRMED, mode: AUTO)
→ Automation worker picks up the order and begins provisioning
```
### 8.2 Outbound Webhooks (Hub Sends)
The Hub can push events to tenant VPS Safety Wrappers and to configured external endpoints.
**To Safety Wrapper:**
| Event | Method | Path (on tenant) | Payload |
|-------|--------|-------------------|---------|
| Config updated | POST | `{safetyWrapperUrl}/webhooks/config-update` | `{ configVersion, changedFields }` |
| Approval response | POST | `{safetyWrapperUrl}/webhooks/approval-response` | `{ approvalId, action, respondedBy }` |
| Remote command | POST | `{safetyWrapperUrl}/webhooks/command` | `{ commandId, type, payload }` |
**Webhook security:** All outbound webhooks include:
- `X-LetsBe-Signature`: HMAC-SHA256 of the request body using the shared Hub API key
- `X-LetsBe-Timestamp`: Unix timestamp (for replay protection)
- `X-LetsBe-Event`: Event type string
### 8.3 Safety Wrapper Webhook Endpoints
Endpoints exposed by the Safety Wrapper for Hub communication:
| Method | Path | Description |
|--------|------|-------------|
| POST | `/webhooks/config-update` | Hub notifies of config changes |
| POST | `/webhooks/approval-response` | Hub delivers approval/denial for gated commands |
| POST | `/webhooks/command` | Hub pushes remote commands |
| POST | `/webhooks/diun` | Diun container update notifications |
| GET | `/health` | Health check (returns Safety Wrapper + OpenClaw status) |
---
## 9. Cron Endpoints (Hub Internal)
| Method | Path | Description | Schedule |
|--------|------|-------------|----------|
| POST | `/api/v1/cron/stats-collection` | Collect stats from all tenant Portainer instances | Every 15 min |
| POST | `/api/v1/cron/stats-cleanup` | Delete stats older than 90 days | Daily at 03:00 |
| POST | `/api/v1/cron/billing-cycle` | Process monthly billing cycles | Daily at 00:00 |
| POST | `/api/v1/cron/pool-alerts` | Check token pools and send usage alerts | Every hour |
| POST | `/api/v1/cron/approval-expiry` | Expire pending approvals older than 24h | Every hour |
---
## 10. Data Models (Prisma)
### 10.1 Existing Models
| Model | Table | Primary Key | Key Relations |
|-------|-------|-------------|---------------|
| User | users | id (UUID) | → Subscription, Order[], FoundingMember? |
| Staff | staff | id (UUID) | — |
| StaffInvitation | staff_invitations | id (UUID) | — |
| Subscription | subscriptions | id (UUID) | → User |
| Order | orders | id (UUID) | → User, DnsVerification?, ProvisioningJob[], ServerConnection? |
| DnsVerification | dns_verifications | id (UUID) | → Order, DnsRecord[] |
| DnsRecord | dns_records | id (UUID) | → DnsVerification |
| ProvisioningJob | provisioning_jobs | id (UUID) | → Order, JobLog[] |
| JobLog | job_logs | id (UUID) | → ProvisioningJob |
| ProvisioningLog | provisioning_logs | id (UUID) | → Order |
| TokenUsage (legacy) | token_usage | id (UUID) | → User |
| RunnerToken | runner_tokens | id (UUID) | — |
| ServerConnection | server_connections | id (UUID) | → Order |
| RemoteCommand | remote_commands | id (UUID) | → ServerConnection |
| SystemSetting | system_settings | id (UUID) | — |
### 10.2 New Models (v1.2 Architecture)
| Model | Table | Primary Key | Key Relations |
|-------|-------|-------------|---------------|
| TokenUsageBucket | token_usage_buckets | id (UUID) | → User, Order |
| BillingPeriod | billing_periods | id (UUID) | → User, Subscription |
| FoundingMember | founding_members | id (UUID) | → User |
| AgentConfig | agent_configs | id (UUID) | → Order |
| CommandApproval | command_approvals | id (UUID) | → Order |
### 10.3 Enterprise/Monitoring Models
| Model | Table | Primary Key | Key Relations |
|-------|-------|-------------|---------------|
| EnterpriseClient | enterprise_clients | id (UUID) | → EnterpriseServer[] |
| EnterpriseServer | enterprise_servers | id (UUID) | → EnterpriseClient |
| ServerStatsSnapshot | server_stats_snapshots | id (UUID) | → EnterpriseServer, EnterpriseClient |
| ErrorDetectionRule | error_detection_rules | id (UUID) | → EnterpriseClient |
| DetectedError | detected_errors | id (UUID) | → EnterpriseServer, ErrorDetectionRule |
| SecurityVerificationCode | security_verification_codes | id (UUID) | → EnterpriseClient |
| LogScanPosition | log_scan_positions | id (UUID) | — |
| ContainerStateSnapshot | container_state_snapshots | id (UUID) | — |
| ContainerEvent | container_events | id (UUID) | — |
| NotificationSetting | notification_settings | id (UUID) | → EnterpriseClient |
| NotificationCooldown | notification_cooldowns | id (UUID) | — |
| Pending2FASession | pending_2fa_sessions | id (UUID) | — |
---
## 11. Error Codes
| Code | HTTP Status | Description |
|------|------------|-------------|
| `VALIDATION_ERROR` | 400 | Request body failed Zod validation |
| `UNAUTHORIZED` | 401 | Missing or invalid authentication |
| `FORBIDDEN` | 403 | Authenticated but insufficient permissions |
| `NOT_FOUND` | 404 | Resource does not exist |
| `CONFLICT` | 409 | Duplicate resource (e.g., duplicate email) |
| `RATE_LIMITED` | 429 | Too many requests. Check `Retry-After` header |
| `PROVISIONING_FAILED` | 500 | Server provisioning pipeline failed |
| `PORTAINER_UNAVAILABLE` | 502 | Cannot reach tenant Portainer instance |
| `NETCUP_ERROR` | 502 | Netcup API returned an error |
| `STRIPE_ERROR` | 502 | Stripe API returned an error |
| `TENANT_OFFLINE` | 503 | Tenant VPS Safety Wrapper is not responding |
| `INTERNAL_ERROR` | 500 | Unhandled server error |
---
## 12. Implementation Priority
| Phase | Endpoints | Effort |
|-------|-----------|--------|
| **Phase 1: Core** | Auth, Customers, Orders, Provisioning, DNS, Containers, Settings | Existing (retool) |
| **Phase 2: Tenant Comms** | /tenant/register, /heartbeat, /config, /usage, /approval-request | 3 weeks |
| **Phase 3: Customer Portal** | /customer/* endpoints | 3 weeks |
| **Phase 4: Agent Management** | /admin/agents/*, /customer/agents/* | 2 weeks |
| **Phase 5: Billing** | /admin/billing/*, cron jobs, Stripe integration | 3 weeks |
| **Phase 6: Analytics** | /admin/analytics/*, customer usage dashboards | 2 weeks |
---
## 13. Changelog
| Version | Date | Changes |
|---------|------|---------|
| 1.0 | 2026-02-26 | Initial API reference. 80+ existing endpoints documented. New endpoint groups: Tenant Communication (7), Customer Portal (16), Agent Management (5), Billing (4), Approvals (3), Analytics (3). Authentication flows. Webhook specs. Data models. Error codes. Implementation priorities. |