One OpsAPI codebase carries every module. Each deployment picks which ones exist with a single environment variable, PROJECT_CODE. A preset name such as business or ecommerce maps to a list of feature flags; several presets can be combined with commas (ecommerce,collaboration), and core is always on.
How a preset decides
This is the only hard boundary between module sets. Roles inside a workspace can hide a module from a member (403), and the per-workspace menu can hide a page, but neither removes the route. If a tenant must never reach a module, give it its own deployment with a narrower PROJECT_CODE. A code that isn't a preset name is skipped with a warning, so combine preset names, not individual feature names.
Presets at a glance
The modules
| Module (flag) | What it covers | Example route |
|---|---|---|
| CRM (crm) | Accounts, contacts, deals, pipelines, leads, activities | /api/v2/crm/… |
| Invoicing (invoicing) | Invoice generation, PDFs and payments | /api/v2/invoices |
| Accounting (accounting) | Bookkeeping, VAT returns, trial balance, AI-assisted | /api/v2/accounting/accounts |
| Tax copilot (tax_copilot) | UK tax return agent with HMRC MTD filing | /api/v2/tax/file |
| Bank transactions (bank_transactions) | Bank transaction tracking | /api/v2/bank-transactions |
| Timesheets (timesheets) | Time tracking and approval | /api/v2/timesheets |
| Kanban (kanban) | Projects, boards, sprints, tasks, time tracking | /api/v2/kanban/… |
| Chat (chat) | Slack-like channels, messages, mentions, reactions | /api/chat/channels |
| Notifications (notifications) | Push notifications to devices | — |
| E-commerce (ecommerce) | Stores, products, orders, payments | /api/v2/stores |
| Delivery (delivery) | Delivery partners, requests and pricing | /api/v2/delivery-requests |
| Reviews (reviews) | Product and store reviews | — |
| Shop (shop) | Configurable catalogue, carts, quotes, Stripe checkout | — |
| Billing (billing) | Apps, flat-tier plans, entitlements, licences | /api/v2/billing/plans |
| Hospital and care (hospital) | Hospitals, wards, patients, care plans, medications | /api/v2/hospitals |
| Field service (field_service) | Service jobs, phases, sites, engineer visits | /api/v2/field-service/jobs |
| Academy (academy) | Courses and lessons with rich content | /api/v2/academy/courses |
| CMS (cms) | Website pages and blog: posts, categories, tags, media | /api/v2/cms/posts |
| Forms (forms) | Form builder, public forms, responses | /api/v2/forms |
| Hiring (ats) | Jobs, candidates, AI first-round interviews | /api/v2/ats/… |
| Themes (themes) | Multi-tenant theming | /api/v2/themes/… |
| Vault (vault) | Secret vault | /api/v2/vault |
| Services (services) | GitHub workflow integration | /api/v2/namespace/services/… |
| Menu (menu) | Backend-driven navigation for the dashboard | /api/v2/user/menu |
Plugins add to this list without forking: a plugin under projects/<name>/ registers its own feature, routes and migrations at boot.
Calling it from TypeScript
@opsapi/client is a typed client for every endpoint, plugins included. A server or script uses an API key scoped to one workspace and the modules it needs:
import { createClient } from '@opsapi/client';
const opsapi = createClient({
baseUrl: 'https://api.example.com',
token: process.env.OPSAPI_KEY, // an API key or a JWT
namespace: 'acme', // workspace slug or UUID
});
const { data } = await opsapi.GET('/api/v2/customers', { params: { query: { page: 1 } } });Keys are created under My Workspace → API Keys or over the API with scopes such as { customers: ['read', 'create'], invoices: ['read'] }. A route for a module your deployment doesn't include still exists in the types, but the server answers 404.