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

# Set up

> Configure backend-held credentials for embedded-wallet or custom-signer sponsorship.

Sponsorship configuration differs by client. Embedded wallets give Rhinestone a short-lived application token and a separate, single-use sponsorship grant. Custom signer integrations prepare sponsored transactions on a backend that holds the project API key.

<Warning>
  Keep private signing keys and API keys on your backend. Never put either
  credential in browser or mobile code.
</Warning>

## Configure sponsorship

<Tabs>
  <Tab title="Embedded wallet">
    ### Embedded wallets backend

    Embedded wallets use two endpoints on your backend:

    | Endpoint                                | Purpose                                                                                            | Typical lifetime |
    | --------------------------------------- | -------------------------------------------------------------------------------------------------- | ---------------- |
    | `GET /api/sponsorship/access-token`     | Mints the app and project identity token.                                                          | 1 hour           |
    | `POST /api/sponsorship/extension-token` | Mints a single-use grant from the wallet's canonical draft input or prepared transaction envelope. | 5 minutes        |

    Use the values from the JWT key registered on the Dashboard's [**JWT keys** screen](https://dashboard.rhinestone.dev/keys/jwt):

    | Value         | Purpose                                                                  |
    | ------------- | ------------------------------------------------------------------------ |
    | Private JWK   | Signs tokens. Store it only on the backend.                              |
    | Integrator ID | JWT `iss` claim.                                                         |
    | Project ID    | JWT `sub` claim. It must match the project that owns the registered key. |
    | App ID        | A label you choose for the deployment, such as `production`.             |
    | Key ID        | JWT `kid` header. It selects the registered public key.                  |

    `@rhinestone/1auth/server` reads these values from `RHINESTONE_JWT_PRIVATE_KEY`, `RHINESTONE_INTEGRATOR_ID`, `RHINESTONE_PROJECT_ID`, `RHINESTONE_APP_ID`, and `RHINESTONE_KEY_ID`. Its `extensionToken()` helper normalizes both 1auth request shapes to the canonical intent input before signing.

    ```ts theme={null}
    // lib/sponsorship.ts
    import { createSponsorshipSigner } from "@rhinestone/1auth/server";

    export const sponsorshipSigner = createSponsorshipSigner();
    ```

    Mount each operation at the URL used by your client. Authenticate your normal application session before minting:

    ```ts theme={null}
    // app/api/sponsorship/access-token/route.ts
    import { sponsorshipSigner } from "@/lib/sponsorship";

    export async function GET(request: Request) {
      await requireApplicationSession(request);
      return Response.json({ token: await sponsorshipSigner.accessToken() });
    }
    ```

    ```ts theme={null}
    // app/api/sponsorship/extension-token/route.ts
    import { sponsorshipSigner } from "@/lib/sponsorship";

    export async function POST(request: Request) {
      await requireApplicationSession(request);
      const { intentOp } = await request.json();
      return Response.json({
        token: await sponsorshipSigner.extensionToken(intentOp),
      });
    }
    ```

    `requireApplicationSession` represents your application's session validation.

    <Warning>
      This minimal route shape is not a production policy. Configure issuance
      checks, bind the requested account to the authenticated session, and
      understand the current enforcement boundary described in [Policies and
      security](./policies-and-security).
    </Warning>

    ### Embedded wallets client

    Use these endpoint URLs in the shared [client initialization](/wallets/embedded-wallets/accounts#initialize-the-client):

    ```ts theme={null}
    import { OneAuthClient } from "@rhinestone/1auth";

    export const oneAuth = new OneAuthClient({
      providerUrl: "https://passkey.1auth.app",
      clientId: "my-app",
      sponsorship: {
        accessTokenUrl: "/api/sponsorship/access-token",
        extensionTokenUrl: "/api/sponsorship/extension-token",
      },
    });
    ```

    The embedded wallet sends cookies to these endpoint URLs. Your backend still decides how to authenticate the application session and whether to issue a sponsorship grant.

    ### Application authentication is not sponsorship

    The access token identifies the app and project. The embedded wallet needs it for both sponsored and user-paid intents. Only an extension token grants permission to spend sponsorship, and each `jti` can be redeemed once.

    Validate the full request before minting. The embedded intent digest is carried in the grant, but it is not currently a downstream product-policy enforcement boundary. See [Policies and security](./policies-and-security#current-enforcement-boundary) before exposing token endpoints to an untrusted client.
  </Tab>

  <Tab title="Custom signer">
    Run the sponsorship-enabled SDK flow on your backend with its project API key:

    ```ts theme={null}
    import { RhinestoneSDK } from "@rhinestone/sdk";

    const rhinestone = new RhinestoneSDK({
      auth: {
        mode: "apiKey",
        apiKey: process.env.RHINESTONE_API_KEY!,
      },
    });
    ```

    Authenticate your application's request, validate the account and transaction, then use this backend SDK instance to prepare, sign, and submit according to your signer architecture. Follow the [Custom signer quickstart](/wallets/custom-signer/quickstart) for account initialization; do not duplicate its signer configuration in a browser-facing sponsorship client.

    <Note>
      The SDK's `experimental_jwt` mode authenticates direct clients. From SDK
      2.16.2 it presents the intent extension when it prepares a sponsored
      transaction, but Rhinestone does not yet compare the grant's digest with the
      request (see [Policies and
      security](./policies-and-security#current-enforcement-boundary)), so a
      modified client can spend a grant on a different intent. Do not use it as a
      replacement for the backend API-key flow when requesting sponsorship.
    </Note>
  </Tab>
</Tabs>

Next, choose [which fees to sponsor](./sponsor-fees) and configure [policies and budgets](./policies-and-security).
