Plug-in API overview
org.vibeerp.api.v1 (api.v1 for short) is the only stable contract in vibe_erp. It is the surface that PF4J plug-ins compile against, and the only surface they are permitted to touch. Everything else in the codebase — platform.*, every PBC's internal package, every concrete Spring bean — is internal and may change in any release.
For the architectural reasoning behind api.v1, see ../architecture/overview.md and the full spec at ../superpowers/specs/2026-04-07-vibe-erp-architecture-design.md.
What api.v1 is
- A Kotlin module published to Maven Central as
org.vibeerp:api-v1. - Depends on only Kotlin stdlib and
jakarta.validation. No Spring, no Hibernate, no PF4J types leak through it. - Semver-governed on the
1.xline. Across a major version,api.v1andapi.v2ship side by side for at least one major release window so plug-ins have time to migrate. - The single import surface for plug-ins. The plug-in linter rejects any import outside
org.vibeerp.api.v1.*at install time.
Package layout
org.vibeerp.api.v1
├── core/ Id<T>, Money, Currency, Quantity, UnitOfMeasure, Result<T,E>
├── entity/ Entity, AuditedEntity, FieldType, CustomField, EntityRegistry
├── persistence/ Repository<T>, Query, Page, Transaction, PersistenceExceptions
├── event/ DomainEvent, EventListener, EventBus (with Subscription)
├── security/ Principal, PrincipalId, Permission, PermissionCheck
├── i18n/ MessageKey, Translator, LocaleProvider
├── http/ @PluginEndpoint, RequestContext
├── plugin/ Plugin, PluginContext, PluginManifest, ExtensionPoint,
│ PluginEndpointRegistrar, HttpMethod, PluginRequest,
│ PluginResponse, PluginEndpointHandler, PluginJdbc, PluginRow
├── ext/ Typed cross-PBC facades: ext.identity.IdentityApi,
│ ext.catalog.CatalogApi
├── workflow/ WorkflowTask, TaskHandler, TaskContext
└── form/ FormSchema
A short orientation:
| Package | What it gives you |
|---|---|
core/ |
The primitive value types every plug-in needs: typed Id<T>, Money + Currency, Quantity + UnitOfMeasure, Result<T,E>. No printing concepts. |
entity/ |
Declarative entity model. Entity and AuditedEntity markers, FieldType sealed hierarchy, CustomField and EntityRegistry for the metadata-driven custom fields story. |
persistence/ |
Repository<T>, Query, Page, Transaction, plus the closed PersistenceException hierarchy (OptimisticLockConflictException, UniqueConstraintViolationException, EntityValidationException, EntityNotFoundException). Plug-ins never see Hibernate, JPA, or Spring @Transactional directly. |
event/ |
DomainEvent, EventListener, EventBus with Subscription (closeable) and topic-string + class-keyed subscribe overloads. The primary cross-PBC communication channel. Live as of P1.7.
|
security/ |
Principal (sealed: User, System, PluginPrincipal), PrincipalId, Permission, PermissionCheck. Plug-ins register their own permissions; the role editor auto-discovers them. |
i18n/ |
MessageKey, Translator, LocaleProvider. The only sanctioned way for a plug-in to produce user-facing text. (Real ICU4J Translator impl is P1.6 — the api.v1 contract is locked, the host implementation throws UnsupportedOperationException until then.) |
http/ |
@PluginEndpoint annotation and RequestContext for plug-in HTTP handlers. |
plugin/ |
The plug-in lifecycle: Plugin interface, PluginContext (with live accessors for logger, endpoints, eventBus, jdbc), PluginManifest, ExtensionPoint/@Extension, and the endpoint types: PluginEndpointRegistrar, HttpMethod, PluginRequest, PluginResponse, PluginEndpointHandler, plus the typed-SQL surface PluginJdbc/PluginRow. |
ext/ |
Typed cross-PBC facades. Two are live today: ext.identity.IdentityApi (with UserRef) and ext.catalog.CatalogApi (with ItemRef/UomRef). Future PBCs will add their own under the same pattern. |
workflow/ |
WorkflowTask, TaskHandler, TaskContext (with tenantId removed — single-tenant — and principal(), locale(), correlationId() accessors). The hooks BPMN service tasks call into when Flowable lands (P2.1). |
form/ |
FormSchema — JSON Schema string + UI Schema string + version. Form parsing and rendering happens in the host (server) and the SPA (client). |
The stability contract
| Change | Allowed within 1.x? |
|---|---|
Add a class to api.v1
|
yes |
Add a method to an api.v1 interface (with default impl) |
yes |
Remove or rename anything in api.v1
|
no — major bump |
Change behavior of an api.v1 symbol in a way plug-ins can observe |
no — major bump |
Anything in platform.* or pbc.*.internal.*
|
yes — that is why it is internal |
Practical consequences for plug-in authors:
- A plug-in built against
api.v1version1.4.0will load in any vibe_erp1.xrelease. - A symbol that gets deprecated in
1.xkeeps working until2.0. The plug-in loader emits a warning when a plug-in uses a deprecated symbol — that is the Grade C signal in the extension grading. - A plug-in that reaches into
platform.*orpbc.*.internal.*via reflection is Grade D, unsupported, and rejected by the plug-in linter at install time.
When in doubt about whether something belongs in api.v1: keep it out. Growing the API deliberately later is much cheaper than maintaining a regretted symbol forever.
The A/B/C/D extension grading
From CLAUDE.md guardrail #7. Every extension to vibe_erp falls into one of four grades, ordered from safest to least safe.
| Grade | What it is | Upgrade safety |
|---|---|---|
| A | Tier 1 metadata only — custom fields, forms, workflows, rules, list views, translations entered through the web UI. Stored as rows in metadata__* tables, tagged source = 'user'. |
Always upgrade-safe across any core version. |
| B | Tier 2 plug-in using only the public api.v1 surface. |
Safe within a major version. Loads cleanly across every 1.x release. |
| C | Tier 2 plug-in using deprecated-but-supported api.v1 symbols. |
Safe until the next major. The plug-in loader emits warnings; the plug-in author should migrate before the next major release. |
| D | Tier 2 plug-in reaching into platform.* or pbc.*.internal.* via reflection. |
UNSUPPORTED. The plug-in linter rejects this at install time. Will break on the next core upgrade. |
Two principles follow from the grading:
- Anything a Tier 2 plug-in does should also become possible as a Tier 1 customization over time. Tier 2 is the escape hatch, not the default.
- If you find yourself wanting to do Grade D, the seam is wrong and
api.v1needs to grow — deliberately, with a version bump consideration.
Reference
The full reference for every type, method, and contract in api.v1 is generated from KDoc by Dokka and published alongside each release. This document is the conceptual overview; the generated reference is the authoritative per-symbol documentation. (The Dokka site is wired up as part of the v1.0 documentation deliverable; until then, the source under api/api-v1/src/main/kotlin/org/vibeerp/api/v1/ is the source of truth, and every public type carries a KDoc block — that is part of the PR checklist in ../../CONTRIBUTING.md.)
Where to go next
- Build your first plug-in:
../plugin-author/getting-started.md - Author a workflow:
../workflow-authoring/guide.md - Author a form:
../form-authoring/guide.md - Localize a plug-in:
../i18n/guide.md