Skip to content

Latest commit

 

History

History
218 lines (163 loc) · 10.8 KB

metadata-api.mdx

File metadata and controls

218 lines (163 loc) · 10.8 KB
title Metadata API — read and publish packages
navTitle Metadata & Package API
description REST endpoints for object schemas, metadata types, UI views, and package management.

Manage object schemas, metadata types, UI views, and installed packages over REST. All paths are relative to the base URL (defaults to /api/v1) — see the API Overview for discovery and service availability.

Metadata

Retrieve and manage object schemas, metadata types, and UI views. Always available — provided by the kernel.

A type segment these routes cannot recognise, but which reaches for a metadata type the platform declares, is refused 400 INVALID_REQUEST rather than served — see unrecognised type spelling in the error catalog for the envelope and the correction it names.

GET /meta

List all registered metadata types.

Response: { types: ["object", "view", "plugin", ...] }

The answer carries a weak ETag and Cache-Control: private, no-cache; send the ETag back as If-None-Match and an unchanged list answers 304 with no body.

GET /meta/:type

List all items of a metadata type.

Parameter Location Description
type path Metadata type name — the singular spelling (object, view, flow)

Metadata type names are singular (Prime Directive #3) and this segment is the type name itself, so the spelling to send is object — never objects. The plurals elsewhere in this API name something else: a collection of records (/data/:object) or a package-manifest key (objects: [...]). Neither carries across to a /meta type segment.

Listing applies the ADR-0046 §6.7 audience gate to books and docs, RBAC filtering to privileged apps, and the usual response shaping.

Response: { type: "object", items: [{ name: "account", ... }, ...] }

The list carries a weak ETag and Cache-Control: private, no-cache, and If-None-Match with the current ETag answers 304 with no body. The ETag hashes the list this caller was served, after the audience and RBAC filtering above, so two callers who see different items hold different tags and one caller's tag never revalidates another caller's list. Keep the response in the client only: private means no shared cache may store it.

GET /meta/:type/:name

Get a specific metadata item with optional ETag caching.

Parameter Location Description
type path Metadata type name
name path Item name (snake_case)
If-None-Match header ETag for conditional request

Response: { type: "object", name: "account", item: { ... } }
304 if ETag matches (not modified).

PUT /meta/:type/:name

Create or update a metadata item.

Saving requires the manage_metadata capability. Under a group or isolated tenancy posture it also requires the platform operator, for every type: the environment's metadata is shared by every organization (ADR-0131 D12 items 7–8, D11), so a manage_metadata holder inside one organization is refused 403 FORBIDDEN naming the posture. The same holds for DELETE (reset), POST /meta/:type/:name/publish and POST /meta/:type/:name/rollback, and for POST /meta/_migrate-stored, the install-wide rewrite of the stored metadata, preview included.

Creates are gated per type: MetadataTypeRegistryEntry.allowRuntimeCreate decides whether a brand-new name may be minted under that type at all, separately from whether an item that already exists may be overlaid (allowEnvironmentOverlay). A type that declines the create half still accepts updates to names that already exist, and its refusal — 403 NOT_CREATABLE — names the route that does work. The flags are documented on MetadataTypeRegistryEntry; which types currently decline which half is tracked in the overlay whitelist rather than enumerated on this page, so this caveat doesn't go stale the next time a type is retired.

This verb also refuses a :type segment that is not a metadata type the platform declares at all — not a recognised spelling, and not a misspelling of one either — with 400 INVALID_REQUEST, before any namespace is minted for it. The refusal is scoped to this verb: GET and DELETE on an item that already exists are unaffected. See unrecognised type spelling in the error catalog for the envelope — the same code and status cover both this refusal and the misspelling one above; the message text tells them apart.

An item an installed package ships is customized in that package's pair (ADR-0146 D2, D11). A PUT naming no package onto an item one installed read-only package hotcrm ships is refused 403 NOT_OVERRIDABLE; for a type an override applies to, the sentence names the route: save it with ?package=hotcrm.custom, the body declaring overrides: { package: 'hotcrm', basedOn: '3.2.0' } (basedOn is the manifest.version of hotcrm it was taken from, recorded and never judged). Only the pair declares an override: an item of any other package that depends on hotcrm and bears a hotcrm item's name is refused at save and publish, and its fix is a new name. A permission set, position or capability is never overridden: a permission set or position is copied under a new name, and a capability changes only with a new version of the package that declares it. An overrides on a type that takes none is refused 403 NOT_OVERRIDABLE, and the sentence names that type's own route. What the reads then serve is in Metadata lifecycle. POST /packages/hotcrm/ensure-customization creates the pair when absent (Studio calls it on the first change), and GET /packages/hotcrm/customizations lists its overrides, extensions and switched-off items in one read, beside contributions: the fields (kind field) and flows (kind flow) other installed packages contribute to hotcrm's objects, and the navigation items (kind nav) they contribute to hotcrm's apps, each entry naming its package.

Body: { type: "object", name: "account", item: { ... } }
Response: { success: true }

GET /ui/view/:object/:type

Get an auto-generated UI view for an object.

Parameter Location Description
object path Object name (snake_case)
type path list or form

Response: Full ViewSchema definition with columns/fields derived from the object schema.


Package Management

Manage installed plugins/packages.

GET /packages

List installed packages.

POST /packages

Install a package from its manifest (SDK: client.packages.install). Re-installing an already-installed id returns 409 Conflict unless overwrite: true.

Body: { manifest: { id: "com.objectstack.plugin-auth", name: "Plugin Auth", version: "1.0.0", ... }, settings?: { ... }, enableOnInstall?: true, overwrite?: false }
Response: { package: { id: "com.objectstack.plugin-auth", version: "1.0.0", ... }, message?: "..." }

POST /packages/publish

Publish a package (manifest + metadata) to the package marketplace registry. This is publisher tooling, not part of the app SDK surface.

Body: { manifest: { id: "com.objectstack.plugin-auth", name: "Plugin Auth", version: "1.0.0", ... }, metadata: { objects: [...], views: [...], ... } }
Response: { success: true, message: "...", package: { id: "com.objectstack.plugin-auth", version: "1.0.0" } }

GET /packages/:id

Get package details.

DELETE /packages/:id

Uninstall a package.

Under a group or isolated tenancy posture every /packages write is the platform operator's (ADR-0131 D12 items 7–8, D11): install (POST /packages), the manifest PATCH, enable, disable, publish, publish-drafts, discard-drafts, revert, rollback, commits/:commitId/revert, duplicate, adopt-orphans and this delete. Any other caller, a manage_metadata holder inside one organization included, is refused 403 PERMISSION_DENIED naming the posture, before anything is read or written, whatever the package carries. Reads are unchanged. Under single every manage_metadata holder writes as before.

PATCH /packages/:id/enable

Enable a disabled package.

The mirror of the disable door's refusal below: the enable is refused 409 PACKAGE_DISABLE_RESTRICTED_BY_VALIDATION_RULE while a validation rule it would put into service (the package's own, or one on an object it owns) reads a field that another, still disabled package adds through objectExtensions: that field stays withheld, and every write to the object would fail the rule. The message names each rule as package · object · rule (reads FIELD) and the disabled package to enable first, and nothing changes. A rule is judged as the object will serve it once the package is enabled: one rule per name, a tenant overlay's switch honoured. A rule the enable does not bring into service is not counted. POST /packages answers the same 409 for an overwrite of a disabled package with enableOnInstall: true; the overwrite lands and the package stays disabled. A boot whose stored state leaves such a rule served (the state records only disables) logs one warning naming every rule and the remedies.

PATCH /packages/:id/disable

Disable a package without uninstalling.

A package that adds fields to another object through objectExtensions is refused 409 PACKAGE_DISABLE_RESTRICTED_BY_VALIDATION_RULE while a validation rule of any enabled package reads one of those fields (declared dependent or not, an app's .custom pair included, any severity): withheld, the field could not be read, and every write to that object would fail the rule. The message names each rule as package · object · rule (reads FIELD), and nothing changes. A rule is judged as the object serves it: one rule per name, a tenant overlay's switch honoured. Switch off or change each named rule first, or disable the package that owns it. A formula over the field does not refuse the disable; it answers null. POST /packages answers the same 409 for an overwrite of an enabled package with enableOnInstall: false that would leave such a package disabled; the overwrite lands and the package stays enabled. A fresh install with enableOnInstall: false lands disabled, as before.


See also

  • API Overview — discovery, error handling, and protocol types
  • Data API — CRUD, batch operations, and analytics