Official TypeScript/JavaScript SDK for Rise's payment and webhook systems, providing both webhook validation and API client functionality.
- 📝 Comprehensive TypeScript Types - Full type definitions for all Rise webhook events and API endpoints
- ✅ Webhook Validation - Validate webhook payloads against Zod schemas with signature verification
- 🚀 API Client - Type-safe HTTP client for all Rise API endpoints
- 🎯 Type Safety - Full type support for all Rise webhook events and API requests/responses with autocomplete
- 🔒 Security First - Built-in signature verification to ensure webhooks come from Rise
- ⚡ Optimized Builds - Lightweight and fast library
npm install @riseworks/sdk
# or
yarn add @riseworks/sdk
# or
pnpm add @riseworks/sdk
import { WebhookValidator, type RiseWebhookEvent } from '@riseworks/sdk';
const riseValidator = new WebhookValidator({
secret: process.env.RISE_WEBHOOK_SECRET!,
tolerance: 720 // 12 minutes tolerance
});
// Validate webhook signature and payload
const event = riseValidator.validateEvent(req.body, req.headers['x-rise-signature']);
console.log(`Event type: ${event.event_type}`);
console.log(`Event ID: ${event.id}`);
For production use, verify that webhook requests actually come from Rise using the WebhookValidator
class:
import express from 'express';
import { WebhookValidator, type RiseWebhookEvent } from '@riseworks/sdk';
const app = express();
// Use raw body for signature verification
app.use('/rise-webhooks', express.raw({ type: 'application/json' }));
app.post('/rise-webhooks', (req, res) => {
const signature = req.headers['x-rise-signature'] as string;
const secret = process.env.RISE_WEBHOOK_SECRET!;
try {
// Validate webhook using Rise SDK WebhookValidator with full type safety
const riseValidator = new WebhookValidator({
secret: secret,
tolerance: 720 // 12 minutes tolerance
});
// Option 1: validateEvent (throws on failure)
let event = riseValidator.validateEvent(req.body, signature);
// Option 2: validateEventSafe (returns result object)
const result = riseValidator.validateEventSafe(req.body, signature);
if (!result.isValid) {
console.error('Validation failed:', result.error);
return res.status(400).json({ error: result.error });
}
event = result.event;
// Signature is valid, process the event with full TypeScript support
handleEvent(event);
res.status(200).json({
received: true,
timestamp: new Date().toISOString(),
event_type: event.event_type,
event_version: event.event_version,
idempotency_key: event.idempotency_key
});
} catch (error) {
console.error('Webhook verification failed:', error instanceof Error ? error.message : String(error));
res.status(400).json({
error: 'Webhook processing failed',
message: error instanceof Error ? error.message : String(error),
timestamp: new Date().toISOString()
});
}
});
import type {
RiseWebhookEvent,
PaymentSentV1,
PaymentGroupCreatedV1,
DepositReceivedV1
} from '@riseworks/sdk';
function handleEvent(event: RiseWebhookEvent) {
switch (event.event_type) {
case 'payment.sent':
// Send confirmation email with full type safety
sendPaymentConfirmation(event as PaymentSentV1);
break;
case 'payment.group.created':
// Update order status with full type safety
updateOrderStatus(event as PaymentGroupCreatedV1);
break;
case 'deposit.received':
// Trigger fulfillment with full type safety
triggerFulfillment(event as DepositReceivedV1);
break;
default:
console.log('Unhandled event type:', event.event_type);
}
}
async function sendPaymentConfirmation(event: PaymentSentV1) {
// Your email logic here with full type safety
console.log(`Sending confirmation for payment ${event.payment.nanoid}`);
console.log(`Amount: ${event.payment.amount_cents} cents`);
console.log(`Recipients: ${event.payment.recipients.length}`);
}
The Rise API Client provides type-safe methods to interact with all Rise API endpoints.
import { RiseApiClient } from '@riseworks/sdk';
// Initialize the API client
const riseClient = new RiseApiClient({
jwtToken: process.env.RISE_JWT_TOKEN!,
timeout: 30000, // Optional, defaults to 30 seconds
});
// Get current user information
const userResponse = await riseClient.me.get();
console.log('Current user:', userResponse.data.user);
console.log('Company:', userResponse.data.company);
// Send invites to team members with full type safety
const inviteResponse = await riseClient.invites.send({
nanoid: 'team_1234567890',
invites: [
{
email: 'john@example.com',
prefill: {
first_name: 'John',
last_name: 'Doe',
phone: '+1234567890',
company_name: 'Example Corp',
job_title: 'Developer',
company_size: '10-50',
company_website: 'https://example.com',
company_industry: 'Technology',
company_address: '123 Main St',
company_city: 'San Francisco',
company_state: 'CA',
company_zip: '94105',
company_country: 'US',
},
},
],
role: 'team_employee',
});
console.log('Invites sent:', inviteResponse.data.nanoids);
// Step 1: Create payments (get typed data for blockchain)
const createResponse = await riseClient.payments.create({
from: 'team_1234567890',
to: [
{
to: 'user_1234567890',
amount_cents: 100000, // $1000.00
currency_symbol: 'USD',
invoice_description: 'Payment for services',
},
],
pay_now: true,
network: 'arbitrum',
});
console.log('Typed data for blockchain:', createResponse.data);
// Step 2: Execute payments (after signing the typed data)
const executeResponse = await riseClient.payments.execute({
from: 'team_1234567890',
to: [
{
to: 'user_1234567890',
amount_cents: 100000,
currency_symbol: 'USD',
invoice_description: 'Payment for services',
},
],
pay_now: true,
network: 'arbitrum',
signer: '0x1234567890123456789012345678901234567890',
typed_data: createResponse.data, // The typed data from step 1
signature: '0x...', // The signature from the wallet
});
console.log('Payment executed:', executeResponse.data);
// Get payments for a team with full type safety
const paymentsResponse = await riseClient.payments.get({
team_nanoid: 'team_1234567890',
state: 'all',
start_date: '2024-01-01',
end_date: '2024-12-31',
query_type: 'payable',
});
console.log('Payments:', paymentsResponse.data.items);
// Send webhook events to Rise (B2B API)
const webhookResponse = await riseClient.webhooks.sendEvent({
event_type: 'payment.sent',
event_version: '1.0',
id: 'evt_1234567890',
timestamp: Math.floor(Date.now() / 1000),
data: {
payment: {
nanoid: 'pay_1234567890',
amount_cents: 100000,
currency_symbol: 'USD',
recipients: ['user_1234567890'],
},
},
});
console.log('Webhook event sent:', webhookResponse);
The Rise API client uses JWT token authentication. You need to provide a JWT token when initializing the client.
import { RiseApiClient } from '@riseworks/sdk'
const client = new RiseApiClient({
jwtToken: 'your-jwt-token-here',
timeout: 30000, // Default: 30 seconds
})
Class for validating webhook signatures and payloads.
new WebhookValidator(options: {
secret: string;
tolerance?: number; // Default: 300 seconds (5 minutes)
})
Validates a webhook event and returns the parsed event.
Parameters:
-
payload
: Raw webhook payload (string, Buffer, or object) -
signature
: Thex-rise-signature
header value
Returns: The validated and typed webhook event
Throws: Error if validation fails
Safely validates a webhook event and returns a result object.
Parameters:
-
payload
: Raw webhook payload (string, Buffer, or object) -
signature
: Thex-rise-signature
header value
Returns: { isValid: boolean; event?: RiseWebhookEvent; error?: string }
Class for making type-safe API calls to Rise endpoints.
new RiseApiClient(options: {
// Authentication (choose one):
jwtToken?: string; // JWT token for authentication
riseIdAuth?: { // Rise ID authentication configuration
riseId: string; // Rise ID for automatic JWT generation
privateKey: string; // Private key for automatic JWT generation
};
// Configuration:
environment?: 'dev' | 'stg' | 'prod'; // Default: 'prod'
baseUrl?: string; // Custom base URL (overrides environment)
timeout?: number; // Default: 30000ms
headers?: Record<string, string>; // Custom headers
})
The Rise API client supports two authentication methods:
1. JWT Token (Traditional)
const client = new RiseApiClient({
jwtToken: 'your-jwt-token-here',
environment: 'prod'
})
2. Rise ID + Private Key (Automatic JWT Generation)
const client = new RiseApiClient({
riseIdAuth: {
riseId: '0x2DF5A64B859B203752C30F941035e3cfE93Bb089',
privateKey: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
},
environment: 'dev'
})
When using Rise ID and private key authentication, the client will:
- Automatically generate a JWT token using SIWE (Sign-In with Ethereum) before making any requests
- Check JWT validity before each request
- Automatically refresh the JWT token if it expires or becomes invalid
- Handle 401 errors by regenerating the JWT token and retrying the request
The client is organized into logical groups for better discoverability and organization:
riseClient.webhooks
- Webhook Operations:
-
sendEvent(data)
- Send webhook events to Rise
riseClient.company
- Company Management:
-
getUsers(params)
- Get company users -
updateAddress(params, data)
- Update company address -
getDetails(params)
- Get organization details -
updateDetails(params, data)
- Update organization details -
getSettings(params)
- Get company settings -
updateSettings(params, data)
- Update company settings -
getRoleSettings(params)
- Get company role settings -
updateRoleSettings(params, data)
- Update company role settings -
getContacts(params)
- Get organization contacts -
updateContacts(params, data)
- Update organization contacts -
getOwnership(params)
- Get organization ownership -
updateOwnership(params, data)
- Update organization ownership
riseClient.entityBalance
- Entity Balance:
-
get(params)
- Get entity balance
riseClient.invites
- Team Invitations:
-
send(data)
- Send invites to team members -
sendManager(data)
- Send manager invites -
executeManager(data)
- Execute manager invites -
get(params)
- Get invites for a company or team
riseClient.me
- Current User:
-
get()
- Get current user information
riseClient.payments
- Payment Operations:
-
get(params)
- Get payments -
create(data)
- Create payments -
execute(data)
- Execute payments
riseClient.payroll
- Payroll Management:
-
enable(data)
- Enable payroll for a team -
getTeamPayroll(params)
- Get team payroll -
getPayrollPeriod(params)
- Get payroll period
riseClient.team
- Team Management:
-
update(data)
- Update team information
riseClient.user
- User Management:
-
updateAddress(data)
- Update user address -
updateAvatar(data)
- Update user avatar -
getCompanies()
- Get user companies -
getTeams()
- Get user teams
riseClient.withdraw
- Withdraw Operations:
-
getSignData(params)
- Get withdraw sign data -
getFee(params)
- Get withdraw fee -
create(data)
- Create withdraw request -
execute(data)
- Execute withdraw
All methods are fully typed with request and response types generated from the API schemas.
// Check authentication method
client.isUsingJwtToken() // Returns true if using JWT token
client.isUsingRiseIdAuth() // Returns true if using Rise ID + private key
// Get current token/info
client.getToken() // Get current JWT token
client.getRiseId() // Get Rise ID (if provided)
// Update authentication
client.updateToken(newToken) // Update JWT token
// Manual JWT refresh (for Rise ID auth)
await client.refreshJwtToken() // Manually refresh JWT token
await client.generateJwtToken() // Generate new JWT token
// Environment management
client.getEnvironment() // Get current environment
client.updateEnvironment(env) // Update environment
The SDK supports all Rise webhook events with full type safety:
-
payment.sent
- A payment has been successfully sent -
payment.group.created
- A new payment group has been created -
deposit.received
- A deposit has been received and confirmed -
invite.accepted
- A team member has accepted an invitation - And many more...
When using Rise ID and private key authentication, the client follows this process:
- SIWE Message Request: Requests a SIWE (Sign-In with Ethereum) message from the Rise API
- Message Signing: Signs the message using the provided private key
- Nonce Extraction: Extracts the nonce from the SIWE message
- Signature Verification: Sends the signed message to the Rise API for verification
- JWT Generation: Receives a JWT token upon successful verification
- Automatic Refresh: Checks JWT validity before each request and refreshes when needed
The process is fully automatic and transparent to the user.
-
Always verify signatures - Use
WebhookValidator
for production webhooks - Use HTTPS - Always use HTTPS in production
- Set appropriate tolerance - Use 5-15 minutes tolerance for timestamp validation
- Handle errors gracefully - Log validation failures for debugging
- Use environment variables - Store webhook secrets securely
- Secure private keys - Never expose private keys in client-side code or logs
- Use environment-specific keys - Use different private keys for dev/staging/production
MIT - See LICENSE file for details