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

# SDK Quickstart

> Get started with the Profy SDK: install, initialize, OAuth authorization, and event reporting.

# SDK Quickstart

The official TypeScript SDK handles OAuth token management and event reporting so you can focus on your app.

## Install

<CodeGroup>
  ```bash npm theme={null}
  npm install @profy-ai/sdk
  ```

  ```bash bun theme={null}
  bun add @profy-ai/sdk
  ```
</CodeGroup>

## Initialize

```typescript theme={null}
import { ProfyApp } from "@profy-ai/sdk";

const profy = new ProfyApp({
  clientId: process.env.PROFY_APP_ID!,
  clientSecret: process.env.PROFY_APP_SECRET!,
  baseUrl: "https://app.profy.cn",       // optional, defaults to profy.cn
  onTokenRefresh: (newToken) => {         // optional, fires on token rotation
    saveToDatabase(newToken);
  },
});
```

## Exchange Authorization Code

After the user authorizes, your callback URL receives a `code` parameter. Exchange it for tokens on the backend:

```typescript theme={null}
const token = await profy.exchangeCode(code, redirectUri);
// → { accessToken, refreshToken, expiresAt }
```

| Field          | Type   | Description                            |
| -------------- | ------ | -------------------------------------- |
| `accessToken`  | string | JWT, valid for 1 hour                  |
| `refreshToken` | string | Valid for 90 days, rotates on each use |
| `expiresAt`    | number | Expiration timestamp (ms)              |

<Warning>
  `exchangeCode` must be called server-side. Never expose App Secret in frontend code.
</Warning>

## Report Billing Events

```typescript theme={null}
const result = await profy.reportEvent("generate_report", {
  token,
  idempotencyKey: "order-12345",  // optional, prevents duplicate charges
  metadata: { plan: "pro" },      // optional, for tracking
});
```

The SDK automatically handles token expiration: when the Access Token expires, it refreshes using the Refresh Token and retries.

## Error Handling

```typescript theme={null}
import {
  AuthExpired,
  InsufficientBalance,
  InvalidEvent,
  ProfyApiError,
} from "@profy-ai/sdk";

try {
  await profy.reportEvent("action", { token });
} catch (err) {
  if (err instanceof AuthExpired) {
    // Token expired and refresh failed → re-authorize
  } else if (err instanceof InsufficientBalance) {
    // User needs to top up credits
  } else if (err instanceof InvalidEvent) {
    // Event name not configured in Studio
  } else if (err instanceof ProfyApiError) {
    console.error(err.statusCode, err.body);
  }
}
```

## Error Reference

| Error Class           | HTTP Status | Trigger                       | Action               |
| --------------------- | ----------- | ----------------------------- | -------------------- |
| `AuthExpired`         | 401         | Token expired, refresh failed | Re-authorize         |
| `InsufficientBalance` | 402         | Insufficient credits          | Prompt top-up        |
| `InvalidEvent`        | 400         | Event name not registered     | Check meter config   |
| `ProfyApiError`       | \*          | Generic API error             | Handle by statusCode |

## Full Example

→ [profy-app-template](https://github.com/user/profy-app-template) (Vite + Hono full-stack scaffold)

<CardGroup cols={2}>
  <Card title="Integration Tutorial" icon="rocket" href="/en/documentation/integration-quickstart">
    Complete integration walkthrough in 5 minutes
  </Card>

  <Card title="API Reference" icon="code" href="/en/api/oauth">
    Full OAuth and Events API documentation
  </Card>
</CardGroup>
