@riseworks/sdk
v1.1.3
Published
Rise SDK for webhook validation and TypeScript types
Readme
Rise SDK
Official TypeScript/JavaScript SDK for Rise B2B integrations.
It includes:
- A typed API client for B2B and
v2endpoints - Claude-style AI coding skills you can copy into
.claude/skills/ - Built-in webhook validation
Installation
npm install @riseworks/sdkQuick Start
import { RiseApiClient } from '@riseworks/sdk'
const client = new RiseApiClient({
environment: 'stg',
jwtToken: process.env.RISE_JWT_TOKEN!,
})
const me = await client.me.get()
const organizations = await client.user.getOrganizations()AI Coding Skills
The npm package ships reusable Claude-style SKILL.md files for coding assistants.
Add skills from the CLI (no copy-paste):
# Add all skills to all supported agents (default)
npx @riseworks/sdk add-skills
# Add to one agent only
npx @riseworks/sdk add-skills --agent cursor rise-sdk-integration rise-webhooks
# List available skills and supported agents
npx @riseworks/sdk add-skills --listOr copy manually from node_modules/@riseworks/sdk/ai-skills/ into .claude/skills/, .cursor/skills/, etc.
Available skills:
rise-sdk-integrationrise-v1-migrationrise-payments-workflowsrise-webhooksrise-teams-and-invitesrise-auth-and-setuprise-security-and-approvalsrise-debugging-and-errors
Use these when you want Claude Code or a similar coding assistant to write better Rise integration code.
Low-Level API Groups
The lower-level client remains available for direct endpoint access.
client.authclient.webhooksclient.companyclient.organizationsclient.entityBalanceclient.invitesclient.meclient.paymentsclient.billPayclient.payrollclient.teamclient.teamsclient.user
Withdrawals are not part of the SDK, and client.company / client.organizations are read-only: those endpoints require reCAPTCHA verification headers that only a browser session can produce, so they cannot be called server-to-server. Use the Rise dashboard for withdrawals and company profile changes.
Every POST/PUT request automatically carries a fresh x-idempotency-key header. Pass your own key to payments.prepare/create/execute, getPaymentTypedData, executePaymentWithSignedData, or sendPayment (as idempotencyKey) when you want retries of the same logical operation deduplicated on your key instead.
Branded ID types
The SDK exports branded nanoid types for API params and responses. Use them when you have a plain string from a response and need to pass it to another method:
| Type | Use case |
|------|----------|
| TeamNanoid | payments.get(), teams.get(), teams.getUsers(), billPay.*, payroll, invites |
| UserNanoid | teams.getMemberSettings(), company members |
| CompanyNanoid | teams.create(), company APIs, webhooks |
| WithdrawAccountNanoid | Typing withdraw-account ids in webhook event payloads |
| WebhookEndpointNanoid | webhooks.get(), webhooks.update(), webhooks.test() |
| WebhookDeliveryNanoid | webhooks.retryDelivery(), delivery history |
| InviteNanoid | Invite execute/list flows |
| TransactionNanoid | Typing payment response transaction fields |
import {
RiseApiClient,
type TeamNanoid,
type UserNanoid,
type CompanyNanoid,
type WithdrawAccountNanoid,
type WebhookEndpointNanoid,
type WebhookDeliveryNanoid,
type InviteNanoid,
type TransactionNanoid,
} from '@riseworks/sdk'
const client = new RiseApiClient({ environment: 'stg', jwtToken: '…' })
const { data } = await client.user.getTeams()
const teamNanoid = data?.teams?.[0]?.nanoid // string
await client.payments.get({
team_nanoid: teamNanoid as TeamNanoid,
state: 'all',
query_type: 'payable',
start_date: new Date(),
end_date: new Date(),
})Examples
Teams
const team = await client.teams.get({ team_nanoid: 'te_123' })
await client.teams.update(
{ team_nanoid: 'te_123' },
{ name: 'Finance Ops' },
)
const members = await client.teams.getUsers({ team_nanoid: 'te_123' })Bill Pay
await client.billPay.createRecipient(
{ team_nanoid: 'te_123' },
{ email: '[email protected]' },
)
const payment = await client.billPay.sendInstantPayment({
from: 'te_123',
amount_cents: 125000,
currency_symbol: 'USD',
external_recipient_email: '[email protected]',
payment_data: {
role_description: 'Design work',
invoice_description: 'Invoice INV-2026-001',
services_description: 'Landing page design',
payment_details: 'Net 15',
rise_sow: false,
},
})Treasury
const balance = await client.entityBalance.get({
nanoid: 'te_123',
})Batch payments
const result = await client.payments.sendPayment({
from: 'te_123' as TeamNanoid,
to: [{ to: 'us_123' as UserNanoid, amount_cents: 50000, currency_symbol: 'USD' }],
pay_now: true,
})
console.log(result.data.transaction)
// Recipients the server skipped or flagged as recently paid are surfaced —
// check them instead of assuming every recipient in `to` was paid.
console.log(result.failed_payments)
console.log(result.duplicates)Before signing, sendPayment verifies the server-provided typed data against your request (expected chain id for the environment, payment count, max per-payment amount) and throws instead of signing anything broader than what you asked for.
Webhooks
import express from 'express'
import { WebhookValidator } from '@riseworks/sdk'
const app = express()
const validator = new WebhookValidator(process.env.RISE_WEBHOOK_SECRET!)
app.post('/rise-webhooks', express.raw({ type: 'application/json' }), (req, res) => {
try {
const event = validator.validateEvent(
req.body,
req.headers['x-rise-signature'] as string,
)
console.log(event.event_type)
res.status(200).json({ received: true })
} catch (error) {
res.status(400).json({
error: error instanceof Error ? error.message : 'Webhook validation failed',
})
}
})Exports
The package exports:
RiseApiClientWebhookValidator- Webhook event types
- Generated API request/response types
Authentication
You can authenticate with either:
jwtTokenriseIdAuthfor automatic SIWE-based JWT generation and refresh
const client = new RiseApiClient({
environment: 'prod',
riseIdAuth: {
riseId: process.env.RISE_ID!,
privateKey: process.env.RISE_PRIVATE_KEY!,
},
})Security:
privateKeyis signing authority over money, not just a login. Anyone who holds it can move funds for the wallet's teams. Use a dedicated wallet, keep the key in a secret manager (never in code or logs), and revoke it in-app if it leaks. See Getting API Access.
Publishing (maintainers)
This package is released manually to the public npm registry — there is no CI workflow for it, unlike @riseworks/contracts. Use pnpm release, which builds, packs and verifies the artifact and refuses to publish one that fails any check. Do not run npm publish or pnpm publish here: both skip the catalog: rewrite, and prepublishOnly is a guard that blocks them.
# 1. Bump above the *published* version (the local file has drifted below it before)
npm view @riseworks/sdk version --registry=https://registry.npmjs.org/
npm version 1.1.3 --no-git-tag-version
# 2. Build, pack and verify. Publishes nothing.
# Add --allow-dirty until the bump above is committed.
pnpm release --allow-dirty
# 3. Commit the bump, then publish the verified artifact
git commit -am "chore(rise-sdk): release v1.1.3"
pnpm release --publishPublishing needs maintainer access to @riseworks/sdk on npmjs.org (ask [email protected]) and a login against that registry explicitly, because the repo .npmrc scopes @riseworks to GitHub Packages:
npm login --registry=https://registry.npmjs.org/pnpm release requires a clean worktree (unless you pass --allow-dirty) and that the local version is strictly greater than the published one; being off main is only a warning. It then runs clean → build → typecheck, packs with pnpm pack (npm pack does not rewrite catalog:), and verifies the tarball before it can leave your machine:
- no
catalog:/workspace:/link:specs in any dependency field a consumer installs exportsdoes not mix condition keys with./subpath keys- every path
package.jsonpoints at —main,module,types, eachbin, everyexportsleaf — exists in the tarball - the shipped
.d.tsfiles only reference packages that are realdependencies. A type-only import of a devDependency (zodis the live risk — the generated API types are Zod-derived) typechecks fine in this workspace and breaks every consumer'stsc - the packed tarball installs into a scratch project, both
importandrequireresolve it and exposeRiseApiClient/WebhookValidator,rise-sdk add-skills --listruns, and a consumer compiles against it withskipLibCheck: false
With --publish it re-runs those install checks against the real registry afterwards, then prints the commit and tag commands for the version bump. Anything that goes wrong after the publish is reported as a warning rather than an error — the version is already public at that point, and a fresh one can 404 for a few seconds, so the check retries and never exits non-zero on a release that actually shipped.
Every check is there because a release already shipped broken: 1.1.0 and 1.1.2 are uninstallable (catalog: leaked into dependencies.ethers) and unimportable (ERR_INVALID_PACKAGE_CONFIG, because exports mixed condition keys with ./src/* subpath keys). A bin-only smoke test could never catch the second one, since bin does not consult exports. You can point the script at any tarball to audit it, including one already on npm:
npm pack @riseworks/[email protected] --registry=https://registry.npmjs.org/
pnpm release --tarball ./riseworks-sdk-1.1.2.tgzTwo warnings are expected and are policy calls rather than release blockers: no LICENSE file ships in the tarball, and engines.node is 26, stricter than the Node this repo builds on — consumers with engine-strict=true cannot install.
License
MIT
