@inkress/admin-sdk
Version:
Official Inkress Commerce API SDK for JavaScript/TypeScript
1,541 lines (1,230 loc) • 40.8 kB
Markdown
# @inkress/admin-sdk
Official Inkress Commerce API SDK for JavaScript/TypeScript applications.
[](https://badge.fury.io/js/@inkress%2Fadmin-sdk)
[](https://www.typescriptlang.org/)
[](https://opensource.org/licenses/MIT)
## Features
- 🚀 **Modern TypeScript SDK** - Built with TypeScript for excellent developer experience
- 🔒 **Secure Authentication** - JWT-based authentication with automatic token management
- 🌐 **Public Endpoints** - Access public merchant information without authentication
- 📦 **Comprehensive API Coverage** - 23+ resources with full CRUD operations
- 🎯 **100% Type-Safe** - Every method fully typed with specific interfaces
- 🔄 **Advanced Query System** - Fluent query builder with intelligent transformations
- 🌍 **Contextual Translations** - Human-readable strings automatically converted to API integers
- 🛠️ **Easy Integration** - Simple setup and intuitive API design
- 🔄 **Automatic Retries** - Built-in retry logic for resilient applications
- 📱 **Cross-Platform** - Works in Node.js, browsers, and React Native
## Installation
```bash
npm install @inkress/admin-sdk
```
```bash
yarn add @inkress/admin-sdk
```
```bash
pnpm add @inkress/admin-sdk
```
## Quick Start
### Basic Setup
```typescript
import { InkressSDK } from '@inkress/admin-sdk';
const inkress = new InkressSDK({
accessToken: 'your-jwt-token',
username: 'merchant-username', // Optional
mode: 'live', // Optional - 'live' (default) or 'sandbox'
});
```
### Configuration Options
```typescript
const inkress = new InkressSDK({
// Required
accessToken: 'your-jwt-token',
// Optional
username: 'merchant-username', // For Client-Id header
mode: 'live', // 'live' = api.inkress.com, 'sandbox' = api-dev.inkress.com
apiVersion: 'v1', // API version (default: 'v1')
timeout: 30000, // Request timeout in ms (default: 30000)
retries: 3, // Number of retry attempts (default: 0)
headers: { // Custom headers for all requests
'X-Custom-Header': 'value'
}
});
```
### Why This SDK?
**🎯 100% Type-Safe** - Every single method across all 23 resources is fully typed:
```typescript
// Every parameter and return value has explicit types
const balances: ApiResponse<MerchantBalance> = await inkress.merchants.balances();
// balances.data: { available: number, pending: number, currency: string }
```
**🌍 Human-Readable API** - Use contextual strings instead of cryptic integers:
```typescript
// Clear, self-documenting code with contextual strings
await inkress.orders.update(123, {
status: 'confirmed',
kind: 'online'
});
// SDK automatically converts to integers for the API
```
**🔍 Powerful Queries** - Intuitive query syntax or fluent builders:
```typescript
// Direct query syntax
await inkress.orders.query({
status: ['confirmed', 'shipped'],
total: { min: 100, max: 1000 },
reference_id: { contains: 'VIP' }
});
// Fluent query builder
await inkress.orders.createQueryBuilder()
.whereStatus(['confirmed', 'shipped'])
.whereTotalRange(100, 1000)
.whereReferenceContains('VIP')
.execute();
```
**📦 Complete Coverage** - 22 resources with 125+ fully-typed methods:
- Core: Merchants, Products, Categories, Orders, Users
- Billing: Plans, Subscriptions, Payment Links, Payment Methods
- Financial: Accounts, Requests, Fees, Currencies, Exchange Rates
- Identity: Addresses, Tokens, Webhooks
- Content: Public Data
- And more...
---
### Public Endpoints (No Authentication Required)
```typescript
// Get public merchant information
const merchant = await inkress.public.getMerchant({
username: 'merchant-username'
});
// Get merchant products
const products = await inkress.public.getMerchantProducts('merchant-username', {
limit: 20,
search: 'laptop'
});
// Get merchant fees
const fees = await inkress.public.getMerchantFees('merchant-username', {
currency: 'JMD',
total: 1000
});
```
### Authenticated Operations with Contextual Values
The SDK automatically translates human-readable strings to API integers:
```typescript
// Create order with contextual strings
const order = await inkress.orders.create({
total: 99.99,
currency_code: 'USD',
status: 'pending', // SDK converts 'pending' → integer
kind: 'online', // SDK converts 'online' → integer
customer: {
email: 'customer@example.com',
first_name: 'John',
last_name: 'Doe'
},
reference_id: 'order-123'
});
// Update merchant with contextual values
const merchant = await inkress.merchants.update(123, {
status: 'approved', // SDK converts to integer
platform_fee_structure: 'customer_pay', // SDK converts to integer
provider_fee_structure: 'merchant_absorb'
});
// Create user with contextual status and kind
const user = await inkress.users.create({
email: 'user@example.com',
first_name: 'John',
last_name: 'Doe',
password: 'secure-password',
status: 'pending', // SDK converts to account_pending integer
kind: 'organisation' // SDK converts to user_organisation integer
});
```
### Advanced Query System
Use the intuitive query system for powerful filtering:
```typescript
// Query with multiple conditions
const orders = await inkress.orders.query({
status: ['confirmed', 'shipped'], // Array → IN query
total: { min: 100, max: 1000 }, // Range query
reference_id: { contains: 'VIP' }, // String search
inserted_at: { after: '2024-01-01' },
page: 1,
page_size: 20
});
// Or use fluent query builder
const products = await inkress.products
.createQueryBuilder()
.whereStatus('published')
.wherePriceRange(50, 500)
.whereTitleContains('laptop')
.whereCategory(5)
.paginate(1, 20)
.orderBy('price', 'desc')
.execute();
```
## Configuration Options
| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| `bearerToken` | `string` | Yes | - | JWT token for API authentication |
| `clientId` | `string` | No | - | Client ID for merchant-specific requests (format: `m-{username}`) |
| `endpoint` | `string` | No | `https://api.inkress.com` | API endpoint URL |
| `apiVersion` | `string` | No | `v1` | API version to use |
| `timeout` | `number` | No | `30000` | Request timeout in milliseconds |
| `retries` | `number` | No | `3` | Number of retry attempts for failed requests |
| `headers` | `Record<string, string>` | No | `{}` | Custom headers to include with requests |
## API Resources
The SDK provides access to 23+ fully-typed resources:
### Core Resources
- **Merchants** - Merchant management and account operations
- **Products** - Product catalog with full CRUD
- **Categories** - Product categorization
- **Orders** - Order processing and tracking
- **Users** - User and account management
### Billing & Subscriptions
- **Billing Plans** - Subscription plan management
- **Subscriptions** - Recurring billing and subscriptions
- **Payment Links** - Payment link generation
- **Payment Methods** - Payment method configuration
### Financial
- **Financial Accounts** - Account management
- **Financial Requests** - Payout and withdrawal requests
- **Transaction Entries** - Transaction tracking
- **Fees** - Fee management and configuration
- **Exchange Rates** - Currency exchange rates
- **Currencies** - Multi-currency support
### Identity & Access
- **Addresses** - Address management
- **Tokens** - API token management
- **Webhook URLs** - Webhook configuration
### Content & Other
- **Generics** - Dynamic endpoint access
- **KYC** - Know Your Customer operations
- **Payout** - Payout processing
- **Public** - Public-facing merchant data
---
## Core Resource Examples
### Merchants Resource
### Merchants Resource
Full merchant management with contextual translations and account methods:
```typescript
// List merchants with contextual filtering
const merchants = await inkress.merchants.list({
status: 'approved', // Contextual: converts to integer
platform_fee_structure: 'customer_pay',
organisation_id: 123,
q: 'coffee shop'
});
// Query merchants with advanced filtering
const merchants = await inkress.merchants.query({
status: ['approved', 'active'],
platform_fee_structure: 'customer_pay',
inserted_at: { after: '2024-01-01' }
});
// Get merchant details
const merchant = await inkress.merchants.get(merchantId);
// Create merchant with contextual values
const newMerchant = await inkress.merchants.create({
name: 'My Store',
email: 'store@example.com',
username: 'mystore',
status: 'pending', // Contextual
platform_fee_structure: 'customer_pay',
provider_fee_structure: 'merchant_absorb'
});
// Update merchant
await inkress.merchants.update(merchantId, {
name: 'Updated Store Name',
status: 'approved'
});
// Merchant account methods (properly typed)
const balances = await inkress.merchants.balances();
// Returns: { available: number, pending: number, currency: string }
const limits = await inkress.merchants.limits();
// Returns: { transaction_limit: number, daily_limit: number, monthly_limit: number, currency: string }
const subscription = await inkress.merchants.subscription();
// Returns: { plan_name: string, status: string, billing_cycle: string, price: number, ... }
const invoices = await inkress.merchants.invoices();
// Returns: MerchantInvoice[]
const invoice = await inkress.merchants.invoice('invoice-123');
// Returns: MerchantInvoice
// Query builder
const merchants = await inkress.merchants
.createQueryBuilder()
.whereStatus('approved')
.wherePlatformFeeStructure('customer_pay')
.whereOrganisation(123)
.search('electronics')
.execute();
```
### Products Resource
Complete product management with status translations and advanced querying:
```typescript
// List products with contextual filtering
await inkress.products.list({
status: 'published', // Contextual: 'published' instead of integer
category_id: 1,
price: { min: 50, max: 500 },
q: 'laptop'
});
// Query products with advanced filters
const products = await inkress.products.query({
status: ['published', 'featured'], // Array query
category_id: [1, 2, 3],
price: { min: 100 },
title: { contains: 'gaming' },
inserted_at: { after: '2024-01-01' }
});
// Get product details
await inkress.products.get(productId);
// Create product with contextual status
await inkress.products.create({
name: 'Gaming Laptop',
description: 'High-performance gaming laptop',
price: 1299.99,
category_id: 1,
status: 'draft', // Contextual
kind: 'published' // Contextual
});
// Update product
await inkress.products.update(productId, {
price: 1199.99,
status: 'published' // Contextual translation
});
// Delete product
await inkress.products.delete(productId);
// Query builder
const products = await inkress.products
.createQueryBuilder()
.whereStatus('published')
.wherePriceRange(50, 500)
.whereCategory(5)
.whereTitleContains('laptop')
.paginate(1, 20)
.orderBy('price', 'desc')
.search('gaming')
.execute();
```
### Categories Resource
Category management with kind translations:
```typescript
// List categories with contextual filtering
await inkress.categories.list({
kind: 'published', // Contextual translation
q: 'electronics'
});
// Query categories
const categories = await inkress.categories.query({
kind: ['published', 'featured'],
parent_id: 1
});
// Get category details
await inkress.categories.get(categoryId);
// Create category with contextual kind
await inkress.categories.create({
name: 'Electronics',
description: 'Electronic devices',
kind: 'published', // Contextual
parent_id: null
});
// Update category
await inkress.categories.update(categoryId, {
name: 'Updated Name',
kind: 'featured' // Contextual
});
// Query builder
const categories = await inkress.categories
.createQueryBuilder()
.whereKind('published')
.whereParent(1)
.search('electronics')
.execute();
```
### Orders Resource
Order processing with full status and kind translations:
```typescript
// Create order with contextual strings
await inkress.orders.create({
total: 99.99,
currency_code: 'USD',
customer: {
email: 'customer@example.com',
first_name: 'John',
last_name: 'Doe'
},
reference_id: 'order-123',
kind: 'online', // Contextual: converts to integer
status: 'pending' // Contextual: converts to integer
});
// Get order details
await inkress.orders.get(orderId);
// Update order status with contextual string
await inkress.orders.update(orderId, {
status: 'confirmed' // Contextual translation
});
// List orders with contextual filtering
await inkress.orders.list({
status: 'shipped', // Contextual
kind: 'online', // Contextual
customer_id: 123,
q: 'electronics'
});
// Query orders with advanced filters
const orders = await inkress.orders.query({
status: ['confirmed', 'shipped'],
kind: ['online', 'subscription'],
total: { min: 100, max: 1000 },
reference_id: { contains: 'VIP' },
inserted_at: { after: '2024-01-01' }
});
// Delete order
await inkress.orders.delete(orderId);
// Get order status (public endpoint)
await inkress.orders.getStatus(orderId);
// Query builder
const orders = await inkress.orders
.createQueryBuilder()
.whereStatus(['confirmed', 'shipped'])
.whereKind('online')
.whereTotalRange(100, 1000)
.whereReferenceContains('PREMIUM')
.whereCustomer(123)
.paginate(1, 20)
.execute();
```
### Users Resource
User management with status and kind translations:
```typescript
// List users with contextual filtering
await inkress.users.list({
status: 'approved', // Contextual: account_approved
kind: 'organisation', // Contextual: user_organisation
organisation_id: 123,
q: 'admin'
});
// Query users
const users = await inkress.users.query({
status: ['approved', 'active'],
kind: ['organisation', 'merchant'],
inserted_at: { after: '2024-01-01' }
});
// Get user details
await inkress.users.get(userId);
// Create user with contextual values
await inkress.users.create({
email: 'user@example.com',
first_name: 'John',
last_name: 'Doe',
password: 'secure-password',
status: 'pending', // Contextual
kind: 'organisation' // Contextual
});
// Update user with contextual status
await inkress.users.update(userId, {
first_name: 'Jane',
status: 'approved' // Contextual
});
// Delete user
await inkress.users.delete(userId);
// Query builder
const users = await inkress.users
.createQueryBuilder()
.whereStatus('approved')
.whereKind('organisation')
.whereOrganisation(123)
.search('john')
.execute();
```
### Billing Plans Resource
Billing plan management with kind translations:
```typescript
// List billing plans
await inkress.billingPlans.list({
kind: 'subscription', // Contextual
status: 'active'
});
// Query plans
const plans = await inkress.billingPlans.query({
kind: 'subscription',
public: true,
amount: { min: 10, max: 100 }
});
// Get plan details
await inkress.billingPlans.get(planId);
// Create billing plan with contextual kind
await inkress.billingPlans.create({
name: 'Premium Plan',
amount: 29.99,
currency: 'USD',
kind: 'subscription', // Contextual
status: 'active'
});
// Update plan
await inkress.billingPlans.update(planId, {
amount: 24.99,
status: 'active'
});
// Delete plan
await inkress.billingPlans.delete(planId);
// Query builder
const plans = await inkress.billingPlans
.createQueryBuilder()
.whereKind('subscription')
.wherePublic(true)
.whereAmountRange(10, 50)
.execute();
```
### Subscriptions Resource
Subscription management with proper typing for all methods:
```typescript
// List subscriptions
await inkress.subscriptions.list({
status: 'active', // Contextual
billing_plan_id: 1,
customer_id: 123
});
// Query subscriptions
const subscriptions = await inkress.subscriptions.query({
status: ['active', 'trialing'],
billing_plan_id: [1, 2, 3],
inserted_at: { after: '2024-01-01' }
});
// Get subscription details
await inkress.subscriptions.get(subscriptionId);
// Create subscription with contextual values
await inkress.subscriptions.create({
billing_plan_id: 1,
record: 'customer',
record_id: 123,
start_date: '2024-01-01',
status: 'active', // Contextual
kind: 'recurring' // Contextual
});
// Delete subscription
await inkress.subscriptions.delete(subscriptionId);
// Create subscription link (fully typed)
const link = await inkress.subscriptions.createLink({
reference_id: 'sub-123',
title: 'Premium Subscription',
plan_uid: 'plan-abc',
customer: {
first_name: 'John',
last_name: 'Doe',
email: 'john@example.com'
}
});
// Returns: CreateSubscriptionLinkResponse
// Charge subscription (fully typed)
const charge = await inkress.subscriptions.charge('sub-uid', {
reference_id: 'charge-123',
total: 29.99,
title: 'Monthly charge'
});
// Returns: ChargeSubscriptionResponse with typed transaction
// Record usage (fully typed)
const usage = await inkress.subscriptions.usage('sub-uid', {
reference_id: 'usage-123',
total: 5.00,
title: 'API calls'
});
// Returns: SubscriptionUsageResponse
// Cancel subscription (fully typed)
const cancelled = await inkress.subscriptions.cancel(123, 'reason-code');
// Returns: SubscriptionCancelResponse
// Get subscription periods
const periods = await inkress.subscriptions.getPeriods('sub-uid', {
status: 'paid',
limit: 10
});
// Query builder
const subscriptions = await inkress.subscriptions
.createQueryBuilder()
.whereStatus('active')
.whereBillingPlan(1)
.whereCustomer(123)
.execute();
```
---
## Additional Resources
### Payment Links
```typescript
// List payment links
await inkress.paymentLinks.list({ status: 'active' });
// Create payment link
await inkress.paymentLinks.create({
title: 'Product Payment',
amount: 99.99,
currency: 'USD',
status: 'active'
});
// Update payment link
await inkress.paymentLinks.update(linkId, { amount: 89.99 });
// Delete payment link
await inkress.paymentLinks.delete(linkId);
```
### Financial Accounts
```typescript
// List financial accounts
await inkress.financialAccounts.list();
// Create account
await inkress.financialAccounts.create({
name: 'Main Account',
type: 'checking',
currency: 'USD'
});
// Update account
await inkress.financialAccounts.update(accountId, { name: 'Updated Name' });
```
### Tokens
```typescript
// List tokens
await inkress.tokens.list({ kind: 'api', enabled: true });
// Create token
await inkress.tokens.create({
title: 'Production API Key',
provider: 'stripe',
kind: 'api'
});
// Delete token
await inkress.tokens.delete(tokenId);
```
### Addresses
```typescript
// List addresses
await inkress.addresses.list({ country: 'US' });
// Create address
await inkress.addresses.create({
line1: '123 Main St',
city: 'New York',
state: 'NY',
country: 'US',
postal_code: '10001'
});
// Update address
await inkress.addresses.update(addressId, { line1: '456 Broadway' });
// Delete address
await inkress.addresses.delete(addressId);
```
### Fees
```typescript
// List fees
await inkress.fees.list({ active: true });
// Create fee
await inkress.fees.create({
name: 'Processing Fee',
amount: 2.50,
percentage: 2.9,
kind: 'transaction'
});
// Update fee
await inkress.fees.update(feeId, { amount: 2.75 });
// Delete fee
await inkress.fees.delete(feeId);
```
### Exchange Rates & Currencies
```typescript
// List currencies
await inkress.currencies.list();
// Create currency
await inkress.currencies.create({
code: 'USD',
name: 'US Dollar',
symbol: '$'
});
// List exchange rates
await inkress.exchangeRates.list({ from_currency: 'USD' });
// Create exchange rate
await inkress.exchangeRates.create({
from_currency: 'USD',
to_currency: 'EUR',
rate: 0.85
});
// Update exchange rate
await inkress.exchangeRates.update(rateId, { rate: 0.86 });
```
### Public Resource
Access public merchant information without authentication:
```typescript
// Get merchant by username or domain
await inkress.public.getMerchant({ username: 'merchant-name' });
await inkress.public.getMerchant({ 'domain.cname': 'store.example.com' });
// Get merchant products with filtering
await inkress.public.getMerchantProducts('merchant-name', {
search: 'laptop',
category: 'electronics',
limit: 20
});
// Get merchant fees (fully typed)
const fees = await inkress.public.getMerchantFees('merchant-name', {
currency: 'USD',
total: 100
});
// Returns: PublicMerchantFees
```
## Advanced Query System
The SDK provides a powerful type-safe query system with two interfaces:
### 1. Direct Query Method
Use the `query()` method with an intuitive object-based syntax:
```typescript
// Simple equality
await inkress.orders.query({
status: 'confirmed',
customer_id: 123
});
// Array queries (IN operations)
await inkress.orders.query({
status: ['confirmed', 'shipped', 'delivered'],
id: [1, 2, 3, 4, 5]
});
// Range queries
await inkress.products.query({
price: { min: 100, max: 1000 },
rating: { min: 4 }
});
// String contains
await inkress.products.query({
title: { contains: 'laptop' },
description: { contains: 'gaming' }
});
// Date ranges
await inkress.orders.query({
inserted_at: { after: '2024-01-01', before: '2024-12-31' },
updated_at: { on: '2024-06-15' }
});
// Complex combined queries
await inkress.orders.query({
status: ['confirmed', 'shipped'],
kind: 'online',
total: { min: 50, max: 500 },
reference_id: { contains: 'VIP' },
customer_id: [100, 101, 102],
inserted_at: { after: '2024-01-01' },
page: 1,
page_size: 20,
q: 'electronics'
});
```
### 2. Fluent Query Builder
Use the query builder for a fluent, chainable interface:
```typescript
// Products query builder
const products = await inkress.products
.createQueryBuilder()
.whereStatus('published')
.whereStatusIn(['published', 'featured'])
.whereCategory(5)
.whereCategoryIn([1, 2, 3])
.wherePriceRange(50, 500)
.wherePriceMin(50)
.wherePriceMax(500)
.whereTitleContains('gaming')
.whereDescriptionContains('RGB')
.whereCreatedAfter('2024-01-01')
.whereCreatedBefore('2024-12-31')
.whereCreatedBetween('2024-01-01', '2024-12-31')
.paginate(1, 20)
.orderBy('price', 'desc')
.search('laptop')
.execute();
// Orders query builder
const orders = await inkress.orders
.createQueryBuilder()
.whereStatus('confirmed')
.whereStatusIn(['confirmed', 'shipped'])
.whereKind('online')
.whereKindIn(['online', 'subscription'])
.whereTotalRange(100, 1000)
.whereReferenceContains('VIP')
.whereCustomer(123)
.whereCustomerIn([100, 101, 102])
.whereCreatedBetween('2024-01-01', '2024-12-31')
.paginate(1, 20)
.search('electronics')
.execute();
// Merchants query builder
const merchants = await inkress.merchants
.createQueryBuilder()
.whereStatus('approved')
.wherePlatformFeeStructure('customer_pay')
.whereProviderFeeStructure('merchant_absorb')
.whereOrganisation(123)
.whereNameContains('coffee')
.paginate(1, 50)
.execute();
// Users query builder
const users = await inkress.users
.createQueryBuilder()
.whereStatus('approved')
.whereKind('organisation')
.whereOrganisation(123)
.whereEmailContains('@company.com')
.paginate(1, 50)
.search('admin')
.execute();
```
### Query Transformation
The SDK automatically transforms your clean queries into API-compatible format:
```typescript
// You write:
{
status: ['confirmed', 'shipped'],
total: { min: 100, max: 1000 },
reference_id: { contains: 'VIP' }
}
// SDK transforms to:
{
status_in: [4, 7], // Contextual strings → integers with _in suffix
total_min: 100, // min/max → _min/_max suffixes
total_max: 1000,
"contains.reference_id": "VIP" // contains → prefix format
}
```
### Available Query Operations
| Operation | Input Syntax | API Output | Description |
|-----------|-------------|------------|-------------|
| **Equality** | `field: value` | `field: value` | Direct match |
| **Array (IN)** | `field: [1,2,3]` | `field_in: [1,2,3]` | Value in array |
| **Range Min** | `field: {min: 10}` | `field_min: 10` | Minimum value |
| **Range Max** | `field: {max: 100}` | `field_max: 100` | Maximum value |
| **Contains** | `field: {contains: 'text'}` | `"contains.field": "text"` | String contains |
| **Date After** | `field: {after: 'date'}` | `"after.field": "date"` | After date |
| **Date Before** | `field: {before: 'date'}` | `"before.field": "date"` | Before date |
| **Date On** | `field: {on: 'date'}` | `"on.field": "date"` | Exact date |
### Query Builder Methods
All resources with query support provide these methods:
**Equality Methods:**
- `.whereField(value)` - Direct equality
- `.whereFieldIn([values])` - Array IN query
**Range Methods:**
- `.whereFieldRange(min, max)` - Min and max
- `.whereFieldMin(value)` - Minimum value
- `.whereFieldMax(value)` - Maximum value
**String Methods:**
- `.whereFieldContains(text)` - String contains
**Date Methods:**
- `.whereCreatedAfter(date)` - After date
- `.whereCreatedBefore(date)` - Before date
- `.whereCreatedBetween(start, end)` - Date range
- `.whereUpdatedAfter(date)` - After date
- `.whereUpdatedBefore(date)` - Before date
**Utility Methods:**
- `.paginate(page, pageSize)` - Pagination
- `.orderBy(field, direction)` - Sorting
- `.search(query)` - General search (q parameter)
- `.execute()` - Execute the query
### Field Type Validation
The SDK validates field types at runtime to prevent API errors:
```typescript
// Runtime validation ensures correct types
await inkress.products.query({
price: 99.99, // ✅ Number field
unlimited: true, // ✅ Boolean field
category_id: 5, // ✅ Integer field
inserted_at: '2024-01-01' // ✅ String field
});
// Type mismatches are caught early
await inkress.products.query({
price: { contains: 'text' } // ❌ Error: price is numeric, not string
});
```
## Search and Filtering
All list operations support comprehensive search and filtering capabilities:
### General Search with `q`
Use the `q` parameter for intelligent searching across multiple relevant fields:
```typescript
// Search merchants - searches name, email, username, etc.
await inkress.merchants.list({ q: 'john smith' });
// Search products - searches title, description, etc.
await inkress.products.list({ q: 'gaming laptop' });
// Search orders - searches reference ID, customer details, etc.
await inkress.orders.list({ q: 'ORDER-12345' });
```
### String-Based Status and Kind Values
The SDK supports human-readable string values for better code clarity:
```typescript
// Use descriptive strings for merchants
await inkress.merchants.list({
status: 'account_approved',
platform_fee_structure: 'customer_pay',
q: 'electronics'
});
// Filter orders with readable values
await inkress.orders.list({
status: 'order_confirmed',
kind: 'order_online',
q: 'laptop'
});
// Filter products by status
await inkress.products.list({
status: 'product_published',
q: 'smartphone'
});
// Integers also work for backward compatibility
await inkress.merchants.list({
status: 2,
platform_fee_structure: 1
});
```
### Available String Values
**Status Values:**
- Orders: `order_pending`, `order_confirmed`, `order_shipped`, `order_delivered`, `order_cancelled`, etc.
- Accounts: `account_pending`, `account_approved`, `account_suspended`, etc.
- Products: `product_draft`, `product_published`, `product_archived`
- Transactions: `transaction_pending`, `transaction_authorized`, `transaction_captured`, etc.
**Kind Values:**
- Orders: `order_online`, `order_offline`, `order_subscription`, `order_invoice`
- Products: `product_draft`, `product_published`, `product_archived`
- Users: `user_address`, `role_organisation`, `role_store`
- Billing: `billing_plan_subscription`, `billing_plan_payout`
**Fee Structure Values:**
- `customer_pay` - Customer pays the fees
- `merchant_absorb` - Merchant absorbs the fees
### Database Field Filtering
Filter by any database field for precise results:
```typescript
// Filter products by specific criteria
await inkress.products.list({
status: 'product_published', // Published only (string format)
category_id: 5, // Specific category
price: 1000, // Exact price
unlimited: true, // Unlimited quantity
inserted_at: '2024-01-01' // Created after date
});
// Filter merchants by organization
await inkress.merchants.list({
organisation_id: 123,
status: 'account_approved',
platform_fee_structure: 'customer_pay'
});
```
### Combining Search and Filters
Mix general search with specific filters for powerful queries:
```typescript
await inkress.products.list({
q: 'phone', // General search
status: 'product_published', // Published only
category_id: 5, // Electronics category
page: 1, // Pagination
per_page: 20, // Results per page
sort: 'price', // Sort by price
order: 'desc' // Descending order
});
```
### Legacy Search Field
Many resources still support the legacy `search` field for backward compatibility:
```typescript
// Legacy approach (still works)
await inkress.products.list({ search: 'laptop' });
// Recommended approach
await inkress.products.list({ q: 'laptop' });
```
## Error Handling
The SDK provides structured error handling with detailed error information:
```typescript
try {
const product = await inkress.products.get(123);
} catch (error) {
if (error.response?.status === 404) {
console.log('Product not found');
} else if (error.response?.status === 422) {
console.log('Validation errors:', error.response.result);
} else {
console.log('Unexpected error:', error.message);
}
}
```
## TypeScript Support
The SDK is built with TypeScript and provides **100% type safety** across all 128+ methods:
### Complete Type Coverage
Every method has:
- ✅ Fully typed input parameters (no `any` types)
- ✅ Fully typed return values with specific interfaces
- ✅ IDE autocomplete for all fields and methods
- ✅ Compile-time type checking
```typescript
import {
InkressSDK,
// Core types
Product,
Category,
Order,
Merchant,
User,
BillingPlan,
Subscription,
// Create/Update types
CreateProductData,
UpdateProductData,
CreateOrderData,
UpdateMerchantData,
CreateUserData,
// Response types
ApiResponse,
ProductListResponse,
OrderListResponse,
MerchantBalance,
MerchantLimits,
MerchantSubscription,
MerchantInvoice,
// Query types
ProductQueryParams,
OrderQueryParams,
RangeQuery,
StringQuery,
// Enum types
OrderStatus,
OrderKind,
AccountStatus,
UserKind,
FeeStructureKey,
StatusKey,
KindKey
} from '@inkress/admin-sdk';
// All responses are properly typed
const response: ApiResponse<ProductListResponse> = await inkress.products.list();
const products: Product[] = response.result?.entries || [];
// Create operations with full typing
const createData: CreateProductData = {
name: 'Gaming Laptop',
price: 1299.99,
category_id: 1,
status: 'published',
kind: 'published'
};
const product: ApiResponse<Product> = await inkress.products.create(createData);
// Update operations with full typing
const updateData: UpdateMerchantData = {
name: 'Updated Store',
status: 'approved',
platform_fee_structure: 'customer_pay'
};
await inkress.merchants.update(123, updateData);
// Merchant account methods with specific types
const balances: ApiResponse<MerchantBalance> = await inkress.merchants.balances();
// balances.data: { available: number, pending: number, currency: string }
const limits: ApiResponse<MerchantLimits> = await inkress.merchants.limits();
// limits.data: { transaction_limit: number, daily_limit: number, monthly_limit: number, currency: string }
const subscription: ApiResponse<MerchantSubscription> = await inkress.merchants.subscription();
// subscription.data: { plan_name: string, status: string, billing_cycle: string, price: number, ... }
const invoices: ApiResponse<MerchantInvoice[]> = await inkress.merchants.invoices();
// invoices.data: Array of typed MerchantInvoice objects
// Query with full type safety
const orderQuery: OrderQueryParams = {
status: ['confirmed', 'shipped'],
total: { min: 100, max: 1000 },
reference_id: { contains: 'VIP' },
page: 1,
page_size: 20
};
const orders: ApiResponse<OrderListResponse> = await inkress.orders.query(orderQuery);
// Contextual enums for type safety
const status: OrderStatus = 'confirmed'; // Only valid order statuses allowed
const kind: OrderKind = 'online'; // Only valid order kinds allowed
const feeStructure: FeeStructureKey = 'customer_pay'; // Only valid fee structures allowed
```
### Type-Safe Query Building
Query builders provide complete type safety:
```typescript
// Typed query builder - only valid methods for each field type
const products = await inkress.products
.createQueryBuilder()
.whereStatus('published') // String field - contextual value
.whereCategory(5) // Number field - direct value
.wherePriceRange(50, 500) // Number field - range
.whereTitleContains('gaming') // String field - contains
.whereUnlimited(true) // Boolean field - direct value
.whereCreatedAfter('2024-01-01') // Date field - after
.paginate(1, 20)
.execute();
// TypeScript ensures you can't use wrong query types
products
.wherePrice({ contains: 'text' }) // ❌ Compile error: price is numeric
.whereTitle(123); // ❌ Compile error: title is string
```
### Response Type Handling
Handle responses with full type awareness:
```typescript
async function getProduct(id: number): Promise<Product | null> {
try {
const response: ApiResponse<Product> = await inkress.products.get(id);
// TypeScript knows the exact structure
if (response.result) {
return response.result; // Product type
}
return null;
} catch (error) {
console.error('Failed to fetch product:', error);
return null;
}
}
async function listProducts(): Promise<Product[]> {
const response: ApiResponse<ProductListResponse> = await inkress.products.list();
// TypeScript knows ProductListResponse structure
const entries = response.result?.entries || [];
// entries is Product[]
return entries.map(product => ({
...product,
discounted_price: product.price * 0.9 // TypeScript knows price is a number
}));
}
```
### Discriminated Unions
Use TypeScript's discriminated unions for status/kind handling:
```typescript
type OrderStatusAction =
| { status: 'pending', action: 'await_payment' }
| { status: 'confirmed', action: 'process_order' }
| { status: 'shipped', action: 'track_shipment' }
| { status: 'delivered', action: 'request_feedback' };
async function handleOrder(orderId: number, statusAction: OrderStatusAction) {
await inkress.orders.update(orderId, { status: statusAction.status });
// TypeScript narrows the type based on status
switch (statusAction.status) {
case 'pending':
// statusAction.action is 'await_payment'
break;
case 'confirmed':
// statusAction.action is 'process_order'
break;
}
}
```
## Environment Configuration
### Development
```typescript
const inkress = new InkressSDK({
accessToken: process.env.INKRESS_DEV_TOKEN,
mode: 'sandbox',
username: 'mour-dev-merchant-username'
});
```
### Production
```typescript
const inkress = new InkressSDK({
accessToken: process.env.INKRESS_PROD_TOKEN,
mode: 'live',
username: 'your-merchant-username'
});
```
## React/Next.js Integration
### Server-Side Usage (API Routes)
```typescript
// pages/api/products.ts or app/api/products/route.ts
import { InkressSDK } from '@inkress/admin-sdk';
const inkress = new InkressSDK({
accessToken: process.env.INKRESS_TOKEN!,
username: process.env.INKRESS_USERNAME!
});
export async function GET() {
const products = await inkress.products.list();
return Response.json(products);
}
```
### Client-Side Usage (Public Endpoints)
```typescript
// hooks/usePublicMerchant.ts
import { InkressSDK } from '@inkress/admin-sdk';
const sdk = new InkressSDK({
accessToken: '', // Empty for public endpoints
mode: 'live'
});
export async function getPublicMerchant(username: string) {
return await sdk.public.getMerchant({ username });
}
```
## Best Practices
### 1. Environment Variables
Store sensitive configuration in environment variables:
```env
INKRESS_TOKEN=your-jwt-token
INKRESS_USERNAME=your-merchant
```
### 2. Error Handling
Always implement proper error handling:
```typescript
async function fetchProducts() {
try {
const response = await inkress.products.list();
return response.result || [];
} catch (error) {
console.error('Failed to fetch products:', error);
return [];
}
}
```
### 3. Rate Limiting
Be mindful of API rate limits and implement appropriate throttling in your application.
### 4. Caching
Cache frequently accessed data like merchant information and categories:
```typescript
const merchantCache = new Map();
async function getCachedMerchant(username: string) {
if (merchantCache.has(username)) {
return merchantCache.get(username);
}
const merchant = await inkress.public.getMerchant({ username });
merchantCache.set(username, merchant);
return merchant;
}
```
### 4. Webhook Verification
Verify incoming webhook requests from Inkress using HMAC SHA256:
```typescript
import { WebhookUtils } from '@inkress/admin-sdk';
// Method 1: Verify with signature, body, and secret
app.post('/webhooks/inkress', (req, res) => {
const signature = req.headers['x-inkress-webhook-signature'];
const body = JSON.stringify(req.body);
const secret = process.env.INKRESS_WEBHOOK_SECRET;
const isValid = WebhookUtils.verifySignature(body, signature, secret);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process webhook...
res.status(200).json({ received: true });
});
// Method 2: Let the SDK extract everything from the request
app.post('/webhooks/inkress', (req, res) => {
const secret = process.env.INKRESS_WEBHOOK_SECRET;
try {
const { isValid, body } = WebhookUtils.verifyRequest(req, secret);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Parse the verified body
const webhookPayload = WebhookUtils.parsePayload(body);
// Process webhook...
res.status(200).json({ received: true });
} catch (error) {
res.status(400).json({ error: error.message });
}
});
// Method 3: Use Express middleware for automatic verification
import { createWebhookMiddleware } from '@inkress/admin-sdk';
app.use('/webhooks/inkress', createWebhookMiddleware(process.env.INKRESS_WEBHOOK_SECRET));
app.post('/webhooks/inkress', (req, res) => {
// Request is already verified, payload attached to req.webhookPayload
const { webhookPayload } = req;
console.log(`Received event: ${webhookPayload.event.action}`);
res.status(200).json({ received: true });
});
```
**Webhook Signature Format:**
- Header: `X-Inkress-Webhook-Signature`
- Algorithm: HMAC SHA256
- Encoding: Base64
- Equivalent to: `crypto.mac(:hmac, :sha256, secret, body) |> Base.encode64()`
See [examples/webhook-server.ts](examples/webhook-server.ts) for a complete implementation.
## Contributing
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
## Migrating from Older Versions
See [MIGRATION.md](MIGRATION.md) for upgrade instructions and breaking changes between versions.
## Support
- 📚 [API Documentation](https://docs.inkress.com)
- 💬 [Discord Community](https://discord.gg/inkress)
- 🐛 [Issue Tracker](https://github.com/inkress/admin-sdk/issues)
- 📧 [Email Support](mailto:support@inkress.com)
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Changelog
See [CHANGELOG.md](CHANGELOG.md) for a list of changes and version history.
---
Made with ❤️ by the [Inkress](https://inkress.com) team.