> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gavai.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Capabilities

> How pages do things at runtime — typed actions with Zod-validated I/O that pages invoke by name, dispatched to a backend like Stripe, the transactional email gateway, object storage, or Postgres.

export const PageMeta = ({audience, readingTime, prereqs, level}) => {
  const items = [audience && ({
    key: "audience",
    label: "AUDIENCE",
    value: audience
  }), readingTime && ({
    key: "time",
    label: "READ",
    value: readingTime
  }), level && ({
    key: "level",
    label: "LEVEL",
    value: level
  }), prereqs && prereqs.length > 0 && ({
    key: "prereqs",
    label: "PREREQS",
    value: prereqs.join(" · ")
  })].filter(Boolean);
  if (items.length === 0) return null;
  return <div className="gv-page-meta" role="group" aria-label="Page metadata">
      {items.map(item => <div className="gv-page-meta__item" key={item.key}>
          <span className="gv-page-meta__label">{item.label}</span>
          <span className="gv-page-meta__value">{item.value}</span>
        </div>)}
    </div>;
};

export const BrandCard = ({eyebrow, title, href, icon, badge, cta, featured = false, horizontal = false, children}) => {
  const classes = ["gv-card", featured && "gv-card--featured", horizontal && "gv-card--horizontal"].filter(Boolean).join(" ");
  const inner = <>
      {icon && <span className="gv-card__icon" aria-hidden="true">
          <Icon icon={icon} />
        </span>}
      <div className="gv-card__main">
        {(eyebrow || badge) && <div className="gv-card__meta">
            {eyebrow && <Eyebrow label={eyebrow} tone="muted" />}
            {badge && <span className="gv-card__badge">{badge}</span>}
          </div>}
        <h3 className="gv-card__title">{title}</h3>
        {children && <div className="gv-card__body">{children}</div>}
        {cta && <span className="gv-card__cta">
            {cta}
            <svg className="gv-card__cta-arrow" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <line x1="7" y1="17" x2="17" y2="7" />
              <polyline points="7 7 17 7 17 17" />
            </svg>
          </span>}
      </div>
    </>;
  return href ? <a className={classes} href={href}>
      {inner}
    </a> : <div className={classes}>{inner}</div>;
};

export const FeatureTable = ({columns, rows, featuredRow, caption, variant}) => <div className={`gv-feature-table ${variant ? `gv-feature-table--${variant}` : ""}`}>
    <table>
      {caption && <caption className="gv-feature-table__caption">{caption}</caption>}
      <thead>
        <tr>
          {columns.map(col => <th key={col.key} scope="col">
              <span className="gv-feature-table__header">{col.label}</span>
            </th>)}
        </tr>
      </thead>
      <tbody>
        {rows.map((row, i) => {
  const rowClass = [i === featuredRow ? "gv-feature-table__row--featured" : "", row.chosen ? "gv-feature-table__row--chosen" : ""].filter(Boolean).join(" ");
  return <tr key={i} className={rowClass}>
              {columns.map(col => {
    const v = row[col.key];
    if (variant === "tradeoff" && Array.isArray(v)) {
      const pill = col.key === "pros" ? "gv-feature-table__pill--pro" : col.key === "cons" ? "gv-feature-table__pill--con" : "";
      return <td key={col.key}>
                      {v.map((item, j) => <span key={j} className={`gv-feature-table__pill ${pill}`}>{item}</span>)}
                    </td>;
    }
    return <td key={col.key}>{v}</td>;
  })}
            </tr>;
})}
      </tbody>
    </table>
  </div>;

export const Eyebrow = ({label, tone = "default"}) => <div className={`gv-eyebrow gv-eyebrow--${tone}`}>
    <span className="gv-eyebrow__bar" aria-hidden="true" />
    <span className="gv-eyebrow__label">{label}</span>
  </div>;

<Eyebrow label="CONCEPT" />

<PageMeta audience="Developers" readingTime="7 min" prereqs={["PageBlocks"]} level="Concepts" />

Pages need to *do* things — collect a payment, send an email, store a file, query data, identify a signed-in user. Capabilities are how. A capability is a typed action a page invokes by name; the runtime validates the arguments against a Zod input schema, dispatches the call to a backend (a payment provider, the transactional email gateway, object storage, Postgres, the end-user identity service), and validates the result on the way back. By the end of this page you will know what a capability is, the six that ship with every workspace, how a page invokes one, and how versioning protects you from breaking changes.

## A capability invocation, in 10 lines

Here is a `Form` block that emails the visitor a thank-you when they submit:

```json page.json theme={null}
{
  "block": "Form",
  "props": {
    "schema_table": "leads",
    "fields": ["name", "email"],
    "on_submit": {
      "capability": "sendEmail",
      "args": {
        "template": "lead-thankyou",
        "to": "ada@example.com"
      }
    }
  }
}
```

The page declares the capability by name — `sendEmail` — along with the arguments. The runtime takes it from there: it validates the args against `sendEmail`'s input schema, dispatches the call to the transactional email gateway, and returns the result. The page author never writes an SDK call, never holds an email domain credential, never sees a network handle.

The `to` value here is a literal address. In a real Form binding, you'd reference the submitted email field with template syntax — see [the Form block reference](/pageblocks/blocks/form) for the full binding pattern.

## The mental model: a typed bridge

A capability is a **typed bridge** between the declarative block tree and a real-world side effect. The block says *what should happen* (`sendEmail` with these args); the runtime decides *how it happens* (call the email gateway with this domain, these credentials, this template).

```mermaid theme={null}
sequenceDiagram
    participant B as Block (Form)
    participant R as Runtime
    participant S as Backend (email gateway)
    B->>R: invoke "sendEmail" { to, template }
    R->>R: validate args against Zod input schema
    R->>S: send email via gateway
    S-->>R: { messageId }
    R->>R: validate output against Zod output schema
    R-->>B: { messageId }
```

Two properties fall out of that shape:

* **Pages stay declarative.** A block describes its intent; it never imports an SDK or holds a secret. The capability name and args are all the page knows.
* **The runtime owns I/O.** Validation, dispatching, and error handling live in one place. A call that fails validation never reaches the backend.

The analogy ends where flows get rich — capabilities can be retried, replayed in tests, and observed in logs the way an SDK call would be. Drop the bridge metaphor once the shape clicks.

## The 6 capabilities

Every workspace ships with these. Each has a Zod-validated input schema and a Zod-validated output schema; the runtime rejects calls and responses that don't match.

<FeatureTable
  caption="The 6 capabilities that ship with every workspace"
  columns={[
{ key: "capability", label: "Capability" },
{ key: "what", label: "What it does" },
{ key: "backend", label: "Backend" }
]}
  rows={[
{
  capability: "`collectPayment`",
  what: "Open a Stripe Checkout session for a billing plan.",
  backend: "Stripe"
},
{
  capability: "`listPlans`",
  what: "Return the workspace's published billing plans with pricing.",
  backend: "Stripe"
},
{
  capability: "`sendEmail`",
  what: "Send a templated email to a recipient.",
  backend: "Transactional email gateway"
},
{
  capability: "`storeFile`",
  what: "Upload a file and return a signed URL.",
  backend: "Object storage"
},
{
  capability: "`authUser`",
  what: "Return the currently signed-in end-user's profile.",
  backend: "End-user identity service"
},
{
  capability: "`runQuery`",
  what: "Execute a typed query against the workspace's Postgres database.",
  backend: "Managed Postgres"
}
]}
/>

The per-capability reference page is the source of truth for each one's exact input and output shape.

## Two ways a page invokes a capability

### From a block (declarative)

Most invocations come from blocks. A `Form` block declares the capability that fires on submit; the runtime validates the arguments against the input schema before dispatching. The earlier example showed `sendEmail`; the same pattern works for `collectPayment` on a `Cta` button or `storeFile` on a file input.

```json page.json theme={null}
{
  "block": "Form",
  "props": {
    "schema_table": "leads",
    "fields": ["name", "email"],
    "on_submit": {
      "capability": "sendEmail",
      "args": {
        "template": "lead-thankyou",
        "to": "ada@example.com"
      }
    }
  }
}
```

### Across the iframe boundary

When a tenant page runs inside an iframe — for example, embedded on a marketing site — capability calls cross the boundary via a postMessage bridge. The bridge serializes each call to the edge proxy at `{tenant}.gavai.run/_capability`, which forwards it to the runtime. You do not write this plumbing; it is automatic.

```mermaid theme={null}
sequenceDiagram
    participant E as Embedded page (iframe)
    participant P as Edge proxy
    participant R as Runtime
    participant S as Backend
    E->>P: postMessage { capability, args }
    P->>R: POST /_capability { capability, args }
    R->>S: dispatch to backend
    S-->>R: result
    R-->>P: result
    P-->>E: postMessage result
```

The shape of the call is identical to an in-page invocation; only the transport differs. A block that works at `acme.gavai.app` works embedded on `acme.com` without changes.

## Versioning

Capabilities are semver'd. The runtime ships new versions as the platform evolves — sometimes adding optional fields, occasionally introducing breaking changes that get a new major version.

If you want to pin a page to a specific release and avoid breakage when the platform ships a v2, add a `version` field to the invocation:

```json invocation.json theme={null}
{
  "capability": "collectPayment",
  "version": "1.x",
  "args": {  }
}
```

Leave `version` unpinned and the runtime uses the latest stable version. Pin to `1.x` and the runtime guarantees a 1.x release for as long as one exists — useful for long-lived page documents you don't want to revisit when major versions ship.

## Related

<Columns cols={2}>
  <BrandCard eyebrow="REFERENCE" title="Capability reference" href="/capabilities/overview" icon="bolt">
    Per-capability inputs, outputs, and provisioning.
  </BrandCard>

  <BrandCard eyebrow="UI MODEL" title="PageBlocks" href="/concepts/pageblocks" icon="layout-grid">
    The blocks that bind capability invocations to buttons, forms, and detail panels.
  </BrandCard>
</Columns>
