npm package: @paymanai/payman-ts
Description: TypeScript SDK for Payman AI Platform
Author: Payman AI
Latest version: 1.0.16

# Payman SDK TypeScript

This SDK provides a simple way to interact with the Payman AI Platform's API using client credentials (client_credentials) and authorization code (authorization_code) authentication. The SDK automatically handles token management, including fetching and refreshing access tokens.

## Installation

```bash
npm install @paymanai/payman-ts
```

## Environment Setup

Before running the SDK or tests, you need to set up your environment variables. Create a `.env` file in the root directory with the following variables:

```bash
PAYMAN_CLIENT_ID=your-client-id
PAYMAN_CLIENT_SECRET=your-client-secret
```

These credentials are required for both running the SDK and executing tests.

## Testing

The SDK uses Jest for testing. To run the tests:

1. Make sure you have set up your `.env` file with the required credentials
2. Install dependencies:
```bash
npm install
```
3. Run the tests:
```bash
npm test
```

The test suite will verify the SDK's functionality, including authentication, API interactions, and response handling.

## Usage

### Initialization

The SDK provides several ways to initialize the client:

1. **Using client credentials (recommended for server-side applications):**
```typescript
import { PaymanClient } from '@paymanai/payman-ts';

const payman = PaymanClient.withCredentials({
  clientId: "your-client-id",
  clientSecret: "your-client-secret",
  name: "my-client", // optional
  sessionId: "ses-existing-session-id" // optional, for resuming conversations
});
```

2. **Using an authorization code (for OAuth flow):**
```typescript
const payman = PaymanClient.withAuthCode({
  clientId: "your-client-id",
  clientSecret: "your-client-secret",
}, "your-auth-code");
```

3. **Using an access token or refresh token:**
```typescript
const payman = PaymanClient.withToken(
  {
    clientId: "your-client-id",
    name: "my-client", // optional
    sessionId: "ses-existing-session-id" // optional
  },
  {
    accessToken: "your-access-token", // required if refreshToken is not provided
    expiresIn: 3600, // required if accessToken is provided
    refreshToken: "your-refresh-token" // optional, enables automatic refresh. required if accessToken is not provided
  }
);
```

- You can also initialize with only a refresh token:
```typescript
const payman = PaymanClient.withToken(
  {
    clientId: "your-client-id",
  },
  {
    refreshToken: "your-refresh-token"
  }
);
```

4. **Resuming a conversation with an existing session ID:**
```typescript
const payman = PaymanClient.withCredentials({
  clientId: "your-client-id",
  clientSecret: "your-client-secret",
  sessionId: "ses-existing-session-id"
});
```

### Token Management

The SDK automatically manages OAuth tokens for you:
- Fetches an access token during initialization (if needed)
- Refreshes the token before it expires (within 60 seconds of expiry)
- If you provide an expired token or a token with zero/negative expiration, the client will automatically refresh it
- If you provide a refresh token, the client will use it to obtain new access tokens as needed

**Retrieving tokens:**
```typescript
const tokenInfo = await payman.getAccessToken();
console.log(tokenInfo?.accessToken, tokenInfo?.expiresIn);

const refreshToken = payman.getRefreshToken();
console.log(refreshToken);
```

### Making Requests

1. **Get a formatted response (recommended for most use cases):**
```typescript
const response = await payman.ask("How much money do I have in my wallet?");
console.log(response);
```

2. **Get a raw response (when you need the full JSON-RPC response):**
```typescript
const rawResponse = await payman.ask("How much money do I have in my wallet?", {
  outputFormat: 'json'
});
console.log(rawResponse);
```

3. **Streaming request with formatted responses:**
```typescript
await payman.ask("How much money do I have in my wallet?", {
  onMessage: (response) => {
    console.log("Formatted response:", response);
  },
  outputFormat: 'markdown'
});
```

4. **Streaming request with raw responses:**
```typescript
await payman.ask("How much money do I have in my wallet?", {
  onMessage: (response) => {
    console.log("Raw response:", response);
  },
  outputFormat: 'json'
});
```

5. **Start a new session with metadata:**
```typescript
const response = await payman.ask("How much money do I have in my wallet?", {
  newSession: true,
  metadata: { source: "web-app" }
});
```

### Session Management

The SDK automatically manages sessions for you. Each client instance maintains a session ID that persists across requests. You can start a new session at any time by setting `newSession: true` in the options.

The SDK also handles OAuth token management automatically:
- Fetches an access token during initialization
- Refreshes the token before it expires (within 60 seconds of expiry)
- Handles token management transparently

### Resuming Conversations

You can resume conversations across different client instances by using session IDs from previous responses:

```typescript
// Start a conversation
const response1 = await payman.ask("How much money do I have in my wallet?");
const sessionId = response1.sessionId; // Save this for later

// Later, resume the conversation with a new client instance
const payman2 = PaymanClient.withCredentials({
  clientId: "your-client-id",
  clientSecret: "your-client-secret",
  sessionId: sessionId
});
const response2 = await payman2.ask("What did we talk about earlier?");

// Or withToken:
const payman3 = PaymanClient.withToken(
  {
    clientId: "your-client-id",
    sessionId: sessionId
  },
  {
    accessToken: "your-access-token",
    expiresIn: 3600
  }
);

// Get the current session ID from any client instance
const currentSessionId = payman.getSessionId();
```

Session IDs are included in the response and can be used to maintain conversation context across different client instances or application restarts.

### Streaming Responses

When using streaming responses with the `onMessage` callback, you'll receive updates in real-time as they become available. This is useful for:
- Long-running tasks
- Real-time updates
- Progress monitoring
- Handling artifacts as they become available

The streaming response can include different types of events:

1. **Status Updates:**
```typescript
await client.ask("List all payees", {
  onMessage: (response) => {
    if ('status' in response) {
      console.log(`Task status: ${response.status}`);
      console.log(`Is final: ${response.isFinal}`);
    }
  }
});
```

2. **Artifact Updates:**
```typescript
await client.ask("List all payees", {
  onMessage: (response) => {
    if ('artifacts' in response) {
      console.log(`New artifact: ${response.artifacts[response.artifacts.length - 1]}`);
    }
  }
});
```

### Using Metadata

The SDK supports various types of metadata that can be attached to requests:

1. **Request-level metadata:**
```typescript
await client.ask("Pay Tyllen 50$?", {
  metadata: {
    source: "mobile-app",
    userId: "user123",
    requestId: "req456"
  }
});
```

2. **Message-level metadata:**
```typescript
await client.ask("Create a new payee with the email tyllen@paymanai.com", {
  messageMetadata: {
    priority: "high",
    category: "payee creation"
  }
});
```

3. **Part-level metadata:**
```typescript
await client.ask("List all wallets", {
  partMetadata: {
    currency: "USD",
    format: "text"
  }
});
```

## API Reference

### PaymanClient

#### Static Methods

- `withCredentials(config: PaymanConfig): PaymanClient`
  - Creates a client using client credentials
  - `config`: Configuration object containing clientId, clientSecret, and optional environment, name, sessionId

- `withAuthCode(config: PaymanConfig, authCode: string): PaymanClient`
  - Creates a client using an authorization code
  - `config`: Configuration object containing clientId, clientSecret, and optional environment, name, sessionId
  - `authCode`: Authorization code obtained via OAuth

- `withToken(config: PaymanConfig, tokenInfo: { accessToken?: string; expiresIn?: number; refreshToken?: string }): PaymanClient`
  - Creates a client using an existing access token or refresh token
  - `config`: Configuration object containing clientId, and optional environment, name, sessionId
  - `tokenInfo`: Object containing accessToken and its expiration time, or refreshToken

#### Instance Methods

- `ask(text: string, options?: AskOptions): Promise<FormattedTaskResponse | TaskResponse>`
  - Sends a message to the Payman AI Agent
  - `text`: The message or question to send
  - `options`: Optional parameters for the request

- `getAccessToken(): Promise<{ accessToken: string; expiresIn: number } | undefined>`
  - Gets the current access token and its expiration information
  - Returns undefined if no token is set

- `getRefreshToken(): string | undefined`
  - Gets the current refresh token if available

- `isAccessTokenExpired(): boolean`
  - Checks if the current access token has expired
  - Returns true if the token has expired or is about to expire within 60 seconds

- `getSessionId(): SessionId`
  - Gets the current session ID
  - Returns the session ID being used by this client instance

- `getClientName(): string | undefined`
  - Gets the name of the Payman client from the configuration
  - Returns the client name if set in the config, undefined otherwise

## Error Handling

The SDK uses Axios for HTTP requests. All API calls will throw an error if the request fails. You can catch these errors and handle them appropriately:

```typescript
try {
  const response = await client.ask("What's the weather?");
} catch (error) {
  if (error.response) {
    // The request was made and the server responded with a status code
    // that falls out of the range of 2xx
    console.error(error.response.data);
    console.error(error.response.status);
  } else if (error.request) {
    // The request was made but no response was received
    console.error(error.request);
  } else {
    // Something happened in setting up the request that triggered an Error
    console.error('Error', error.message);
  }
}
```