#Shares & Grants
When you build an app or workflow another tenant installs, controlling what those consumers can see and do is part of authoring it. Helm separates two independent levers, and you usually reason about them separately:
| Tier | Lever | Scope | Actions | Where you set it |
|---|---|---|---|---|
| 1 | Share | cross-tenant | read only | owner-side, next to the resource: /definition/*/{id}/shares, /entity/.../shares |
| 2 | Grant | intra-tenant principal | any of CRUD (intra) / read (cross) | /access/grants |
A manifest bundles many shares into one offer you provision to another tenant in a single motion (below).
Authenticate with a Personal Access Token (HTTP Basic auth) — enter it once in the Authorize bar above and every Try it below uses it.
#Tier 1 — Shares (cross-tenant, read-only)
A share is how a tenant that owns a resource (an entity type, a workflow
definition, an app spec, …) declares that a specific grantee tenant may read
it. That's the whole contract — shares are read-only by design:
- ✅ Makes a resource's existence + shape visible to another tenant.
- ✅ Lets the grantee use a shared definition (e.g. activate a shared app spec in their own tenant — the activation is theirs).
- ❌ Never lets the grantee write to the owner's data.
- ❌ Never materializes the owner's data inside the grantee tenant.
- ❌ Never lets the grantee re-share onward.
Wanting
create/updateon a share is a sign you need a different lever: cross-tenant writes are the consumer acting on its own rows of a shared type, or a workflow that runs in the owner's tenant under an owner-controlled policy.
#Entity type shares vs entity (data) shares
For entities, two share kinds are distinct and independent:
- An entity-type share conveys catalog + schema visibility — "this type exists, here's its shape". It never reveals the owner's rows.
- An entity (data) share conveys row visibility — "see my rows". It requires a parent entity-type share.
This is what lets you share a type (so a partner can store their own rows of it) without exposing your rows.
POST/entity/entity-types/{id}/sharesShare a type's definition (cross-tenant, read-only)API docs ↗Try it
POST/entity/entity-types/{id}/data-sharesShare a type's rows (requires the definition share)API docs ↗Try it
#Tier 2 — Grants (intra-tenant authorization)
A grant authorizes a specific principal — a user, a team, or a policy — to
act on a resource, optionally narrowed by an ABAC attribute_filter.
- Intra-tenant grant (you own the resource):
actionsmay be any subset ofcreate/read/update/delete. - Cross-tenant grant (the resource is another tenant's):
actionsis constrained to["read"], and a matching active share must also exist.
POST/access/grantsGrant a principal actions on a resourceAPI docs ↗Try it
#Combining the tiers
Within one tenant, a grant is all you need. For a cross-tenant data read you need both — a tier-1 share (owner → consumer) and a tier-2 grant (consumer → their principal):
tier-1 share (owner → consumer) tier-2 grant (consumer → principal)
A ──(rows of type X visible to tenant B)──► B ──(user u may read type X)──► user u
Remove the share and the cross-tenant read stops on the next request; remove the grant and the principal loses access while the share remains.
#Manifests — provision a bundle
Sharing one resource at a time is fine for a single type. To provision a whole app or workflow package to another tenant, use a manifest: a named bundle of shares, offered with two-party consent — nothing materializes until both sides act.
- The owner publishes the manifest to a specific grantee tenant (an offer).
- The grantee subscribes (accepts). Only now does each shareable item in the bundle become a read-only share inside the grantee's tenant.
Either side can pull out unilaterally — the owner rescinds the publication, or the grantee unsubscribes — and the shares disappear immediately. Every share a manifest produces is read-only, like any share.
#Provision an app spec (auto-derived manifest)
For an app you never hand-author the manifest. Declare what the app exposes in the
spec's top-level shares block — shares.entityTypes (catalog + schema),
shares.jsonSchemas (extra standalone schemas), and shares.entities (the only way
the owner's actual rows become readable, with scope: "all" / "specific"). Then
share the spec with the consumer tenant — which auto-publishes the spec's derived
manifest to them:
POST/definition/specs/{id}/sharesShare an app spec (auto-publishes its manifest)API docs ↗Try it
When the consumer activates the spec (Apps) it
auto-subscribes, so the bundled shares land in their tenant. To read the owner's rows,
the consumer's policy still needs its own entity:<type>:read grant — the share makes
the rows visible, the grant lets a principal read them.
#Provision a workflow
A workflow definition has no auto-derived manifest. Either share it directly (the consumer activates it in their own tenant, where it runs under their own policy):
POST/definition/workflows/{id}/sharesShare a workflow definition directlyAPI docs ↗Try it
…or, when the workflow needs the consumer to also see the entity types, rules, schemas, and jobs it references, bundle them all in a standalone manifest (next).
#Standalone manifests
Author a bundle of resources you own and provision it to a tenant. Each child names a
resource you own (actions is always ["read"]); a child you don't own is rejected
and the whole manifest rolls back. To share row data, carry both an entity_type
child (catalog) and an entity child (rows) for the same type.
POST/access/manifestsAuthor a manifest bundleAPI docs ↗Try it
Publish it to a grantee tenant (an offer — no shares yet):
POST/access/manifests/{manifestId}/publicationsPublish (offer) the manifest to a tenantAPI docs ↗Try it
The grantee tenant's admin then subscribes to accept — and the bundle's shares
materialize in their tenant (offers appear under GET /access/manifests?direction=incoming):
POST/access/manifests/{manifestId}/subscriptionsSubscribe (grantee accepts the offer)API docs ↗Try it
Edit a bundle later with PATCH /access/manifests/{id} (a new children array
replaces the bundle and re-syncs active subscribers). Rescind with PATCH …/publications/{id} { "is_active": false }, or tear down with DELETE.
#See also
Provisioning walks the tenant-admin side (users, tokens, policies); the Canonical Example shows a tier-1 share + tier-2 grant end to end.