# Generative features in your module

> Add a Generate action to a module face. Maru bills the listener's own credits and records your app as the source.

Canonical: https://maruaudio.com/docs/generative-features
Documentation index: https://maruaudio.com/llms.txt

A module face can ask the host to generate audio for the person using it.
The host makes the request with its own sign-in, and Maru meters the job on
that person's account. Your registered app is recorded as the source. The face
never holds a token, a URL or an account identifier. It sees a status, job ids
and progress events.

This is an experimental source-workspace feature. Hosted generation also
requires an available inference service and an account admitted to hosted
generation.

## 1. Register an app for the module

Open [Creator apps](https://maruaudio.com/account/apps) and register an app:

- set **Module** to your module's id, for example `music.example.sdk-graph-effect`;
- tick **inference:run** among the offered permissions, so the app may ask to
  spend credits;
- add at least one callback: the page where your app's own sign-in runs.

The host looks up the app by module id. A module needs exactly one enabled
app. With none, or with more than one, the host reports `unavailable` and does
not generate. Nothing falls back to Maru's own attribution.

## 2. Request the capability

Declare the host capability in the module's UI options:

```ts
ui: {
  entry: "./ui/index.tsx",
  hostCapabilities: { optional: ["app.generation/v1"] },
},
```

Hosts offer `face.appGeneration` only to faces that declare it. It is
optional: a host without it, such as an offline plug-in, omits the prop, and
your face should keep working.

## 3. Generate from the face

```tsx
const generation = face.appGeneration
useEffect(() => generation?.subscribe((event) => setStatus(event.type)), [generation])
const jobId = await generation!.submit({ prompt: "dry punchy drum loop", seconds: 4, audioType: "loop" })
```

`submit` takes a bounded request: a prompt of up to 1000 characters, 1 to 120
seconds, 1 to 8 `items` (variations), an optional model id (`auto`, `sm-sfx`,
`sm-music` or `medium`) and an optional audio type. The face cannot choose
seeds, a payer or an app. `cancel(jobId)` stops a job you submitted.

`subscribe` delivers the events of your own jobs: `queued`, `progress`, `item`,
`item-error`, then one of `done`, `cancelled` or `error`. An `item` event means
the host fetched that take and saved it to the person's generated sounds
library. It carries the seed, model, duration, sample rate and channel count,
not the audio.

`status` tells the face what to show:

| Status | Meaning | Face action |
| --- | --- | --- |
| `ready` | The person can generate now. | Show **Generate**. |
| `needs-sign-in` | Nobody is signed in on this host. | Call `requestAccess()`; the host opens its sign-in. |
| `needs-consent` | The person has not given your app spending permission. | Call `requestAccess()`; the host opens your app. |
| `unavailable` | See `reason`, for example `app-not-registered`. | Explain; do not retry in a loop. |

The SDK Graph Effect reference module shows the complete pattern in
`modules/sdk-graph-effect/src/ui/index.tsx`.

## Consent and billing

Spending permission is the same explicit `inference:run` consent a standalone
app receives. The person grants it once by signing in to your app through
Maru's consent page, which names your app, its owner, the account and its
shared credit balance. Until then the host reports `needs-consent`, and
`requestAccess()` opens your app's first registered callback origin.
Disconnecting the app in [Connected apps](https://maruaudio.com/account/connections), disabling it or
changing its access policy ends that permission for new requests and for
streams already running.

Every job uses the person's existing credit wallet, quotas and admission. The
module page and the Studio preview submit with the website session; your face
code is the same in each host.

## What Usage shows

Each job's usage row and credit receipt record your app's client id. The
person's [Usage](https://maruaudio.com/usage) page groups credit consumption by app, under
your app's current name. Historical rows keep the app id when the app is
renamed or deleted.

## The same code in a standalone app

A creator app that runs outside Maru uses the generation SDK's hosted
connector with an app credential: an authenticated `fetch` and your client id.
The connector adds the app header to every request. With a creator app OAuth
session, `createCreatorAppGenerationConnector` builds that credential from the
session's request function:

```ts
import { createCreatorAppGenerationConnector } from "@maruaudio/gen-sdk/service/hosted"

const cloud = createCreatorAppGenerationConnector({ config, request: session.request })
// equivalent to credentials: { kind: "app", fetch, clientId }
```

See [Build an audio generator](https://maruaudio.com/docs/audio-generator.md) for the
connector itself.
