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

# Quickstart

> Go from zero to a working on-chain subscription in 5 minutes. Install the Vowena SDK, create a billing plan, subscribe a wallet, charge, and cancel.

Get a complete subscription lifecycle running on Stellar in under 5 minutes.

<Info>
  This guide uses the Vowena TypeScript SDK. You'll need Node.js 18+ and a Stellar account with USDC.
</Info>

<Steps>
  <Step title="Install the SDK">
    Add the Vowena SDK to your project.

    <CodeGroup>
      ```bash npm theme={null}
      npm install @vowena/sdk
      ```

      ```bash yarn theme={null}
      yarn add @vowena/sdk
      ```

      ```bash pnpm theme={null}
      pnpm add @vowena/sdk
      ```
    </CodeGroup>

    <Tip>
      The SDK is a lightweight wrapper around the Vowena Soroban contract. It builds transactions locally - you sign and submit them with your own wallet or keypair.
    </Tip>
  </Step>

  <Step title="Create a Client">
    Initialize the `VowenaClient` with your network configuration.

    <Tabs>
      <Tab title="Mainnet">
        ```typescript theme={null}
        import { VowenaClient, NETWORKS } from "@vowena/sdk";

        const client = new VowenaClient({
          contractId: NETWORKS.mainnet.contractId,
          rpcUrl: NETWORKS.mainnet.rpcUrl,
          networkPassphrase: NETWORKS.mainnet.networkPassphrase,
        });
        ```
      </Tab>

      <Tab title="Testnet">
        ```typescript theme={null}
        import { VowenaClient, NETWORKS } from "@vowena/sdk";

        const client = new VowenaClient({
          contractId: NETWORKS.testnet.contractId,
          rpcUrl: NETWORKS.testnet.rpcUrl,
          networkPassphrase: NETWORKS.testnet.networkPassphrase,
        });
        ```
      </Tab>

      <Tab title="Custom RPC">
        ```typescript theme={null}
        import { VowenaClient } from "@vowena/sdk";

        const client = new VowenaClient({
          contractId: "CABC...XYZ",
          rpcUrl: "https://your-rpc.example.com",
          networkPassphrase: "Test SDF Network ; September 2015",
        });
        ```
      </Tab>
    </Tabs>

    <Note>
      `NETWORKS.mainnet` provides pre-configured values for `contractId`, `rpcUrl`, `networkPassphrase`, and `usdcAddress` so you don't need to look them up.
    </Note>
  </Step>

  <Step title="Create a Plan (Merchant)">
    Define a subscription plan. This is an on-chain transaction that stores the plan parameters permanently.

    ```typescript theme={null}
    import { toStroops, NETWORKS } from "@vowena/sdk";

    // Build the create_plan transaction
    const tx = await client.buildCreatePlan({
      merchant: "GMERCHANT...ADDR",        // Merchant's Stellar address
      token: NETWORKS.mainnet.usdcAddress,  // USDC token contract
      amount: toStroops("9.99"),            // 9.99 USDC per period → 99900000n
      period: 2_592_000,                    // 30 days in seconds
      priceCeiling: toStroops("14.99"),     // Max price before re-auth needed
      trialPeriods: 1,                      // 1 free billing period
      maxPeriods: 0,                        // 0 = unlimited
      gracePeriod: 259_200,                 // 3 days grace on failed charges
    });

    // Sign with your wallet and submit
    const signedXdr = await signTransaction(tx); // Your wallet integration
    const result = await client.submitTransaction(signedXdr);
    console.log("Plan created! ID:", result.planId);
    ```

    <Warning>
      Plans are immutable once created. If you need to change pricing, create a new plan and use the migration flow to move existing subscribers.
    </Warning>

    <Accordion title="What does toStroops do?">
      `toStroops("9.99")` converts a human-readable amount to the 7-decimal-place integer format used by Stellar tokens. It returns a `BigInt`:

      ```typescript theme={null}
      toStroops("9.99")   // → 99900000n
      toStroops("1.00")   // → 10000000n
      toStroops("0.50")   // → 5000000n
      ```

      USDC on Stellar uses 7 decimal places (stroops), matching the native XLM precision.
    </Accordion>
  </Step>

  <Step title="Subscribe (Subscriber)">
    A subscriber authorizes the plan. This single transaction calls `subscribe()` on the Vowena contract **and** sets the SEP-41 token allowance - one signature covers both.

    ```typescript theme={null}
    // Build the subscribe transaction
    const tx = await client.buildSubscribe(
      "GSUBSCRIBER...ADDR",  // Subscriber's Stellar address
      planId                  // Plan ID from step 3
    );

    // Subscriber signs and submits
    const signedXdr = await signTransaction(tx);
    const result = await client.submitTransaction(signedXdr);
    console.log("Subscribed! Subscription ID:", result.subscriptionId);
    ```

    <Tip>
      The subscriber's wallet will display exactly what is being approved: the token (USDC), the spending ceiling (price\_ceiling from the plan), and the contract authorized to pull funds. Full transparency - no hidden permissions.
    </Tip>
  </Step>

  <Step title="Charge (Keeper / Anyone)">
    Once a billing period has elapsed, **anyone** can trigger the charge. The contract validates all conditions on-chain.

    ```typescript theme={null}
    // Build the charge transaction
    const tx = await client.buildCharge(
      "GCALLER...ADDR",  // Address of whoever is calling (can be anyone)
      subscriptionId      // Subscription ID from step 4
    );

    // Sign and submit
    const signedXdr = await signTransaction(tx);
    const result = await client.submitTransaction(signedXdr);
    console.log("Charged! Period:", result.periodNumber);
    ```

    The contract checks:

    * <Check /> Is the subscription active?
    * <Check /> Has enough time passed since the last charge?
    * <Check /> Is the trial period over?
    * <Check /> Does the subscriber have sufficient balance and allowance?
    * <Check /> Has the max period limit been reached?

    If all checks pass, USDC is transferred from the subscriber directly to the merchant via `transfer_from()`.

    <Info>
      You don't need to run your own keeper. The [Vowena Dashboard](/dashboard/keeper-setup) includes a built-in keeper service, or you can use third-party keeper networks.
    </Info>
  </Step>

  <Step title="Cancel">
    Either the subscriber **or** the merchant can cancel a subscription at any time.

    ```typescript theme={null}
    // Build the cancel transaction
    const tx = await client.buildCancel(
      "GSUBSCRIBER...ADDR",  // Subscriber or merchant address
      subscriptionId          // Subscription ID
    );

    // Sign and submit
    const signedXdr = await signTransaction(tx);
    const result = await client.submitTransaction(signedXdr);
    console.log("Cancelled! Subscription:", subscriptionId);
    ```

    <Note>
      Cancellation is **immediate and on-chain**. No approval from the merchant is needed. The subscriber can also cancel directly through any Stellar wallet or block explorer that supports Soroban contract invocations - the Vowena frontend is not required.
    </Note>
  </Step>
</Steps>

***

## Full Example

Here's the complete flow in a single script:

```typescript theme={null}
import { VowenaClient, NETWORKS, toStroops } from "@vowena/sdk";

const client = new VowenaClient({
  contractId: NETWORKS.mainnet.contractId,
  rpcUrl: NETWORKS.mainnet.rpcUrl,
  networkPassphrase: NETWORKS.mainnet.networkPassphrase,
});

// 1. Merchant creates a plan
const createTx = await client.buildCreatePlan({
  merchant: "GMERCHANT...ADDR",
  token: NETWORKS.mainnet.usdcAddress,
  amount: toStroops("9.99"),
  period: 2_592_000,
  priceCeiling: toStroops("14.99"),
  trialPeriods: 1,
  maxPeriods: 12,
  gracePeriod: 259_200,
});
const planResult = await client.submitTransaction(await sign(createTx));

// 2. Subscriber subscribes
const subTx = await client.buildSubscribe("GSUB...ADDR", planResult.planId);
const subResult = await client.submitTransaction(await sign(subTx));

// 3. After billing period elapses, anyone charges
const chargeTx = await client.buildCharge("GKEEPER...ADDR", subResult.subscriptionId);
await client.submitTransaction(await sign(chargeTx));

// 4. Subscriber cancels when they want
const cancelTx = await client.buildCancel("GSUB...ADDR", subResult.subscriptionId);
await client.submitTransaction(await sign(cancelTx));
```

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Core Concepts" icon="lightbulb" href="/concepts">
    Understand plans, subscriptions, allowances, and the billing lifecycle in depth.
  </Card>

  <Card title="How It Works" icon="diagram-project" href="/how-it-works">
    Visual walkthrough of the full protocol flow - from plan creation to cancellation.
  </Card>

  <Card title="SDK Reference" icon="npm" href="/sdk/client">
    Complete SDK documentation with all methods, types, and configuration options.
  </Card>

  <Card title="Run a Keeper" icon="robot" href="/sdk/keeper">
    Set up an automated keeper bot to charge subscriptions on schedule.
  </Card>
</CardGroup>
