> ## 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.

# Monetization

> Connect your workspace to Stripe and charge your own end users via Stripe Connect.

export const ErrorTable = ({errors}) => <div className="gv-error-table" role="table" aria-label="Errors and fixes">
    <div className="gv-error-table__head" role="row">
      <span className="gv-error-table__col-head" role="columnheader">Error</span>
      <span className="gv-error-table__col-head" role="columnheader">Cause</span>
      <span className="gv-error-table__col-head" role="columnheader">Fix</span>
    </div>
    {errors.map((err, i) => <div className="gv-error-table__row" role="row" key={i}>
        <code className="gv-error-table__code" role="cell">{err.code}</code>
        <div className="gv-error-table__cause" role="cell">{err.cause}</div>
        <div className="gv-error-table__fix" role="cell">{err.fix}</div>
      </div>)}
  </div>;

export const ExpectedOutput = ({caption = "Expected output", children}) => <div className="gv-output">
    <div className="gv-output__label">
      <svg className="gv-output__icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
        <polyline points="15 10 20 15 15 20" />
        <path d="M4 4v7a4 4 0 0 0 4 4h12" />
      </svg>
      <span>{caption}</span>
    </div>
    <div className="gv-output__body">{children}</div>
  </div>;

export const APIBadge = ({method = "GET", path, scope}) => {
  const m = method.toUpperCase();
  return <span className="gv-api-badge">
      <span className={`gv-api-badge__method gv-api-badge__method--${m.toLowerCase()}`}>
        {m}
      </span>
      <code className="gv-api-badge__path">{path}</code>
      {scope && <span className="gv-api-badge__scope">
          <span className="gv-api-badge__scope-label">scope</span>
          <code>{scope}</code>
        </span>}
    </span>;
};

export const NumberedStep = ({n, title, children}) => <li className="gv-step">
    <span className="gv-step__index">{n}</span>
    <div className="gv-step__body">
      <h3 className="gv-step__title">{title}</h3>
      <div className="gv-step__content">{children}</div>
    </div>
  </li>;

export const NumberedSteps = ({children}) => <ol className="gv-steps">{children}</ol>;

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="RESOURCE" />

Monetization is the inverse of billing. Where [Billing](/api-reference/resources/billing) tracks what gavAI charges you, Monetization is how you charge your own customers. It manages Stripe Connect onboarding for your workspace and exposes the connected account list that powers the [`collectPayment`](/capabilities/collect-payment) and [`listPlans`](/capabilities/list-plans) capabilities at runtime. By the end of this page you will know the onboarding flow, how to read the connection state, and when `collectPayment` becomes usable.

<Note>
  This resource controls **your workspace charging your end users**. For what gavAI charges you, see [Billing](/api-reference/resources/billing).
</Note>

## Onboarding walkthrough

Stripe Connect onboarding must complete before `collectPayment` can process payments. The flow is asynchronous: you fetch a redirect URL, send the workspace owner through Stripe's hosted flow, then poll until `state` is `connected`.

<NumberedSteps>
  <NumberedStep n={1} title="Request the onboarding URL">
    Call <APIBadge method="POST" path="/v1/workspaces/{slug}/monetization" scope="monetization:write" /> — it returns a Stripe Connect onboarding URL. Repeat the call if the link expires — it refreshes the URL.
  </NumberedStep>

  <NumberedStep n={2} title="Send the workspace owner to Stripe">
    Open `onboarding_url` in the owner's browser. Stripe hosts the entire flow — bank details, identity verification, tax info.
  </NumberedStep>

  <NumberedStep n={3} title="Stripe completes or asks for more">
    Stripe redirects back to your platform's `return_url` when the owner finishes or abandons. Stripe may request additional information later; `state` reflects that.
  </NumberedStep>

  <NumberedStep n={4} title="Poll until state is connected">
    Call <APIBadge method="GET" path="/v1/workspaces/{slug}/monetization" scope="monetization:read" /> — it returns the current `state`. Poll until it is `connected`. A webhook event is also fired on state change.
  </NumberedStep>

  <NumberedStep n={5} title="collectPayment is now usable">
    Once `state` is `connected`, [`collectPayment`](/capabilities/collect-payment) can open Stripe Checkout sessions for end users. No further configuration required.
  </NumberedStep>
</NumberedSteps>

## Endpoints

<APIBadge method="GET" path="/v1/workspaces/{slug}/monetization" scope="monetization:read" />

Current Stripe Connect status for the workspace.

<APIBadge method="POST" path="/v1/workspaces/{slug}/monetization" scope="monetization:write" />

Begin or refresh Stripe Connect onboarding.

<APIBadge method="GET" path="/v1/workspaces/{slug}/monetization/accounts" scope="monetization:read" />

List connected Stripe accounts.

## Status shape

<ExpectedOutput>
  ```json theme={null}
  {
    "state":               "connected",
    "stripe_account_id":   "acct_1Pxxxxxxxxxxxxxx",
    "capabilities_enabled": ["collectPayment", "listPlans"]
  }
  ```
</ExpectedOutput>

`stripe_account_id` is `null` before onboarding starts. `capabilities_enabled` lists the platform capabilities the current Connect state unlocks.

## Onboarding URL shape

<ExpectedOutput>
  ```json theme={null}
  {
    "onboarding_url": "https://connect.stripe.com/setup/e/acct_1P.../xxxxx",
    "expires_at":     "2026-05-11T14:30:00Z"
  }
  ```
</ExpectedOutput>

<Note>
  The onboarding URL is a Stripe-generated link with a short expiry. Do not cache it — call POST each time you need a fresh link.
</Note>

## Common errors

<ErrorTable
  errors={[
{ code: "403 insufficient_scope", cause: "Token lacks `monetization:read` or `monetization:write`.", fix: "Mint a token with the scope you need." },
{ code: "404 workspace_not_found", cause: "No workspace with that slug.", fix: "Verify the slug and that the token can see the workspace." },
{ code: "409 already_connected", cause: "The workspace already has an active Connect account.", fix: "Use `/monetization/accounts` to manage the existing account." },
]}
/>

## Related

<Card title="collectPayment capability" icon="credit-card" href="/capabilities/collect-payment">
  Open a Stripe Checkout session for an end user — requires a connected Stripe account.
</Card>
