# 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`](../architecture/overview.md) and the full spec at [`../superpowers/specs/2026-04-07-vibe-erp-architecture-design.md`](../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.x` line. Across a major version, `api.v1` and `api.v2` ship 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/ Tenant, Locale, Money, Quantity, Id, Result ├── entity/ Entity, Field, FieldType, EntityRegistry ├── persistence/ Repository, Query, Page, Transaction ├── event/ DomainEvent, EventListener, EventBus ├── security/ Principal, Permission, PermissionCheck ├── i18n/ MessageKey, Translator, LocaleProvider ├── http/ @PluginEndpoint, RequestContext, ResponseBuilder ├── plugin/ Plugin, PluginManifest, ExtensionPoint ├── ext/ Typed extension interfaces a plug-in implements ├── workflow/ WorkflowTask, WorkflowEvent, TaskHandler └── form/ FormSchema, UiSchema ``` A short orientation: | Package | What it gives you | |---|---| | `core/` | The primitive value types every plug-in needs: `Tenant`, `Locale`, `Money`, `Quantity`, typed `Id`, `Result`. No printing concepts. | | `entity/` | Declarative entity model. Plug-ins describe entities, fields, and field types through `EntityRegistry`; the platform handles persistence and OpenAPI. | | `persistence/` | `Repository`, `Query`, `Page`, `Transaction`. Plug-ins never see Hibernate or Spring `@Transactional` directly. | | `event/` | `DomainEvent`, `EventListener`, `EventBus`. The primary cross-PBC and cross-plug-in communication channel. | | `security/` | `Principal`, `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. | | `http/` | `@PluginEndpoint` and the request/response abstractions for adding REST endpoints from a plug-in. | | `plugin/` | `Plugin`, `PluginManifest`, `ExtensionPoint`, and the `@Extension` annotation. The plug-in lifecycle entry points. | | `ext/` | Typed extension interfaces that PBCs declare and plug-ins implement (e.g. `api.v1.ext.inventory.StockReservationStrategy`). The cross-PBC interaction surface. | | `workflow/` | `WorkflowTask`, `WorkflowEvent`, `TaskHandler`. The hooks BPMN service tasks call into. | | `form/` | `FormSchema`, `UiSchema`. JSON Schema and UI Schema as Kotlin types, for plug-ins shipping form definitions. | ## 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.v1` version `1.4.0` will load in any vibe_erp `1.x` release. - A symbol that gets deprecated in `1.x` keeps working until `2.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.*` or `pbc.*.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: 1. 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. 2. If you find yourself wanting to do Grade D, the seam is wrong and `api.v1` needs 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`](../../CONTRIBUTING.md).) ## Where to go next - Build your first plug-in: [`../plugin-author/getting-started.md`](../plugin-author/getting-started.md) - Author a workflow: [`../workflow-authoring/guide.md`](../workflow-authoring/guide.md) - Author a form: [`../form-authoring/guide.md`](../form-authoring/guide.md) - Localize a plug-in: [`../i18n/guide.md`](../i18n/guide.md)