Important : Rise Webhooks v1 is deprecated and will be discontinued. All customers must migrate to v2 by [DATE] . After this date, v1 webhooks will no longer be supported.
Rise Webhooks v2 introduces new features and enhancements in reliability, security, and event structure. This guide helps you migrate from v1 to the new v2 architecture.
SDK Required : For v2 webhook handling, you’ll need to install the Rise SDK: npm install @riseworks/sdk
Understanding the changes
The migration from v1 to v2 involves fundamental changes to webhook structure, security, and event types.
Structured envelope v2 uses a consistent envelope format for all events with enhanced metadata
Nanoid identifiers Replaced numeric IDs with human-readable nanoids for better tracking
Versioning support Each event includes version information for future compatibility
Enhanced security HMAC-based signature verification with individual customer secrets
Webhook payload structure
v1 (Deprecated)
v2 (Current)
Will be discontinued: This structure will no longer be available after v1 deprecation.
{
"type" : "deposit.deposit_received" ,
"idempotent_key" : "0x012321" ,
"company_id" : 1 ,
"timestamp" : 1700242816 ,
"transaction" : {
// Event-specific data
}
}
{
"object" : "event" ,
"created" : 1751590453 ,
"request_id" : "req-1751590452487" ,
"event_type" : "payment.sent" ,
"event_version" : "1.0" ,
"idempotency_key" : "85420805-0b5e-4b11-b7f4-c6f05db7120b" ,
// Event-specific data structure
}
Security model changes
v1 security (deprecated)
Deprecated: v1 security model will be discontinued with v1 deprecation.
v1 used a public key verification system:
v1 verification (DEPRECATED)
const hash = req . headers [ 'x-rise-hash' ];
const signature = req . headers [ 'x-rise-signature' ];
// Verify hash
const bodyHash = ethers . id ( JSON . stringify ( body ));
if ( bodyHash !== hash ) {
return res . status ( 400 ). send ( 'Invalid hash' );
}
// Verify signature with the public key from your dashboard
const riseSigner = '0x1234' ; // Public key from your Rise dashboard
const recoveredAddress = ethers . verifyMessage ( hash , signature );
if ( recoveredAddress !== riseSigner ) {
return res . status ( 400 ). send ( 'Invalid signature' );
}
v2 security (current)
v2 uses HMAC-based signature verification with your own secret. The Rise SDK provides built-in webhook validation:
v2 verification with SDK (CURRENT)
const { WebhookValidator } = require ( '@riseworks/sdk' );
// Initialize webhook validator with your secret
const validator = new WebhookValidator ({
secret: process . env . RISE_WEBHOOK_SECRET ,
tolerance: 300 // 5 minutes tolerance for timestamp validation
});
// Verify signature and get validated event
const event = validator . validateEvent ( req . body , req . headers [ 'x-rise-signature' ]);
console . log ( 'Validated event:' , event );
Alternative: Safe validation with error handling
v2 safe verification with SDK
const { WebhookValidator } = require ( '@riseworks/sdk' );
const validator = new WebhookValidator ({
secret: process . env . RISE_WEBHOOK_SECRET ,
tolerance: 300
});
try {
// Safe validation that returns null on failure instead of throwing
const event = validator . validateEventSafe ( req . body , req . headers [ 'x-rise-signature' ]);
if ( event ) {
console . log ( 'Validated event:' , event );
// Process the event
} else {
console . error ( 'Invalid webhook signature' );
res . status ( 400 ). send ( 'Invalid signature' );
return ;
}
} catch ( error ) {
console . error ( 'Webhook validation error:' , error . message );
res . status ( 400 ). send ( 'Validation failed' );
return ;
}
Individual secrets Each customer manages their own webhook secret
Replay protection Timestamps prevent old events from being replayed
HMAC verification Industry-standard signature method
Event type mapping
Important: All v1 event types listed below will be discontinued when v1 is deprecated. Ensure you update your integration to handle v2 event types.
v1 Event Type v2 Event Type Changes deposit.deposit_receiveddeposit.receivedSimplified naming, enhanced payload payment.payment_sentpayment.sentCleaner naming, structured data pay_schedules.pay_schedule_createdpayment.group.createdRenamed to reflect payment groups invites.invite_acceptedinvite.acceptedSimplified naming account_duplicated.detectedwithdraw_account.duplicated_detectedMore specific naming payee.riseid_address_updated(Deprecated) No longer supported in v2
Recommended migration strategy
Migration Required : Since v1 will be deprecated, migration to v2 is mandatory, not optional.
We recommend the gradual migration approach to minimize risk and ensure a smooth transition:
Set up parallel webhooks
Support both v1 and v2 webhooks during the transition period: // Support both v1 and v2 webhooks during transition
app . post ( '/webhooks/v1' , handleV1Webhook );
app . post ( '/webhooks/v2' , handleV2Webhook );
function handleV1Webhook ( req , res ) {
// Existing v1 logic with v1 signature verification
const hash = req . headers [ 'x-rise-hash' ];
const signature = req . headers [ 'x-rise-signature' ];
// v1 verification (keep existing logic)
verifyV1Signature ( req . body , hash , signature );
// Convert to v2 format for unified processing
const v2Event = convertV1ToV2 ( req . body );
processWebhookEvent ( v2Event );
res . status ( 200 ). send ( 'OK' );
}
function handleV2Webhook ( req , res ) {
// New v2 logic with SDK
const { WebhookValidator } = require ( '@riseworks/sdk' );
const validator = new WebhookValidator ({
secret: process . env . RISE_WEBHOOK_SECRET ,
tolerance: 300
});
try {
// Validate webhook signature using SDK
const event = validator . validateEvent ( req . body , req . headers [ 'x-rise-signature' ]);
processWebhookEvent ( event );
res . status ( 200 ). json ({ received: true });
} catch ( error ) {
console . error ( 'Webhook validation failed:' , error . message );
res . status ( 400 ). send ( 'Invalid signature' );
}
}
// Convert v1 events to v2 format for unified processing
function convertV1ToV2 ( v1Event ) {
// Your conversion logic here based on event type
return {
object: 'event' ,
created: v1Event . timestamp ,
request_id: `v1- ${ v1Event . idempotent_key } ` ,
event_type: mapV1ToV2EventType ( v1Event . type ),
event_version: '1.0' ,
idempotency_key: v1Event . idempotent_key ,
// Map v1 data to v2 structure
... v1Event . transaction
};
}
function mapV1ToV2EventType ( v1Type ) {
const mapping = {
'deposit.deposit_received' : 'deposit.received' ,
'payment.payment_sent' : 'payment.sent' ,
'pay_schedules.pay_schedule_created' : 'payment.group.created' ,
'invites.invite_accepted' : 'invite.accepted' ,
'account_duplicated.detected' : 'withdraw_account.duplicated_detected'
};
return mapping [ v1Type ] || v1Type ;
}
Update event processing
Create a single event processor that handles both formats: function processWebhookEvent ( event ) {
// Your unified event processing logic here
// Handle both v1 and v2 event formats
}
Create v2 webhook in Rise app
Navigate to webhook settings in the Rise app
Create new webhook with your /webhooks/v2 endpoint
Generate webhook secret and store it securely
Subscribe to same events as your v1 webhook
Test the v2 webhook using Rise’s test feature
Monitor both endpoints
Run both webhooks in parallel while tracking migration progress: // Add monitoring to track migration progress
const migrationStats = {
v1Count: 0 ,
v2Count: 0 ,
v1Errors: 0 ,
v2Errors: 0
};
function handleV1Webhook ( req , res ) {
migrationStats . v1Count ++ ;
try {
// Your v1 processing logic here
const v2Event = convertV1ToV2 ( req . body );
processWebhookEvent ( v2Event );
res . status ( 200 ). send ( 'OK' );
} catch ( error ) {
migrationStats . v1Errors ++ ;
console . error ( 'V1 webhook error:' , error . message );
res . status ( 400 ). send ( 'Error' );
}
}
function handleV2Webhook ( req , res ) {
migrationStats . v2Count ++ ;
try {
// V2 processing with SDK
const { WebhookValidator } = require ( '@riseworks/sdk' );
const validator = new WebhookValidator ({
secret: process . env . RISE_WEBHOOK_SECRET ,
tolerance: 300
});
const event = validator . validateEvent ( req . body , req . headers [ 'x-rise-signature' ]);
processWebhookEvent ( event );
res . status ( 200 ). json ({ received: true });
} catch ( error ) {
migrationStats . v2Errors ++ ;
console . error ( 'V2 webhook error:' , error . message );
res . status ( 400 ). send ( 'Invalid signature' );
}
}
// Log stats periodically
setInterval (() => {
console . log ( 'Migration stats:' , migrationStats );
}, 60000 ); // Every minute
Switch to v2 only
Once you’re confident v2 is working correctly:
Deactivate v1 webhook in the Rise app
Monitor v2 webhook for any issues
Remove v1 endpoint from your code after a few days
Clean up migration code once fully migrated
Migration checklist
Time-Sensitive : Complete migration before v1 deprecation date to avoid service disruption.
Review v1 webhook usage in your application
Identify all event types currently processed
Plan migration strategy
Set up testing environment
Generate webhook secret for v2
Implement v2 webhook endpoint
Add signature verification for v2
Update event processing logic
Test both v1 and v2 events during parallel operation
Monitor webhook delivery success rates
Remove v1 webhook endpoints when no longer needed
Update documentation and team knowledge
Monitor v2 webhook functionality
Remove v1 compatibility code after transition period
Update monitoring and alerting for new event types
Getting help with migration
If you encounter issues during migration:
What’s next?
After completing your migration to v2:
Migration tip : Take your time with migration, but don’t delay too long. It’s better to migrate carefully over a few days than to rush and break production systems. The gradual migration approach allows you to validate each step before proceeding.
Important reminder : v1 webhooks will be discontinued on [DATE] . Ensure your migration is complete before this date to avoid service interruption.