Not yet proven
Five of the nine grant kinds validate and are part of the manifest schema, but no shipped app uses them. There is no production behaviour to describe, no field experience to pass on, and nothing about them that this page will invent.
The five
storage
{ "storage": "serial-certificates", "access": ["read", "write"] }
A bucket name, or * for all of the app's buckets. access is read and/or write.
secret
{ "secret": "/apps/serials/*", "access": ["read"] }
A secret path or path glob the app may read. Read-only for now.
mail
{ "mail": "*" }
An allowed sending domain, or * for the tenant's configured mail domain.
ai
{ "ai": "*" }
An allowed model or model family, or * for any model the tenant has enabled.
inbound
{ "inbound": "acme-service-callbacks" }
An inbound webhook source name the app subscribes to.
What "not yet proven" means concretely
It is worth being precise, because "unproven" is doing real work in that sentence:
- The schema accepts it. Your manifest validates, your app deploys, and the entry appears on the consent screen.
- There is no shipped app to copy. Every production app's register uses
entity,identity,capabilityandoutbound. There is no working example of these five to read. - The runtime behaviour is not described anywhere, including here. How a bucket is addressed, how a secret is read, what a mail send looks like, which models exist, how an inbound callback is routed to your function: none of that is settled enough to write down.
- The details may change before enforcement lands. A design you build on an assumption today may need rewriting.
So the difference between these and outbound is real. outbound is unenforced but exercised — seven platform apps declare accurate egress through it, so the shape of the declaration is settled and only the enforcement layer is outstanding. These five are neither enforced nor exercised.
What to do instead, today
If your app needs one of these resources now, there is usually a shipped path to the same outcome.
| You need | Proven option |
|---|---|
| A credential for an external API | A settings.json key marked sensitive that the merchant fills in, or an Integration Studio credential if the integration lives in a workflow |
| To store a file | The platform's Storage service through its public API, or the customer's own storage via a declared outbound host |
| To send mail | An external provider via a declared outbound host, or an Integration Studio workflow |
| To call a model | An external provider via a declared outbound host |
| To receive a callback from a third party | A POST capability of your own that the third party calls, guarded by whatever signature scheme they offer |
That last row is the one worth reading twice. An inbound webhook does not have to wait for the inbound grant: a capability is a public HTTPS route, so a payment provider or an ERP can call it today. What you take on yourself is verifying the caller — check the provider's signature header in your handler and answer 403 when it does not verify. payments.webhooks.ingest is a designated override point for exactly this shape of problem.
If you declare one anyway
There are good reasons to: the register is the record of what your app is for, and a merchant reading a consent screen benefits from an accurate one.
- Declare narrowly. A named bucket or a specific secret path, not
*. - Do not ship code that assumes it works. Guard the call, handle the failure, and degrade to something sensible.
- Say in your release notes that the feature depends on an unreleased platform capability. A merchant who consents to
aiand finds the feature inert will ask why. - Re-read this page before your next major. The list of five is expected to shrink.
Next steps
- Permissions — the four that are proven.
- Outbound HTTP — the grant most of these workarounds route through.
- Settings — where a merchant-supplied secret goes today.
- Consent — what declaring a grant shows the merchant.