Documentation Index Fetch the complete documentation index at: https://docs.lumx.io/llms.txt
Use this file to discover all available pages before exploring further.
Webhooks notify your application whenever a resource changes status. Instead of polling the API, you receive an HTTP POST request to your endpoint with the updated data.
Setting up webhooks
To configure webhooks, go to Developers > Webhooks in the Dashboard and add your endpoint URL. You can subscribe to specific event types or receive all events.
You must be a project admin or owner to create webhooks.
Event structure
Every webhook event follows a consistent structure with three top-level fields:
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "onramp.success" ,
"data" : {
// Event-specific payload
}
}
Field Type Description eventIdstringUnique identifier for the event (UUID) eventTypestringThe event type (e.g., onramp.success, customer.approved) dataobjectThe event payload, which varies by event type
Available events
Event type Triggered when onramp.awaiting_fundsOn-ramp is waiting for the customer to send funds onramp.transferring_fiatFiat transfer is in progress onramp.tradingTrade is being executed onramp.transferring_stablecoinStablecoin transfer to the customer is in progress onramp.successOn-ramp completed successfully onramp.failedOn-ramp failed onramp.expiredOn-ramp expired before payment was received
Event type Triggered when offramp.transferring_stablecoinStablecoin transfer from the customer is in progress offramp.tradingTrade is being executed offramp.transferring_fiatFiat transfer to the destination is in progress offramp.successOff-ramp completed successfully offramp.failedOff-ramp failed
Event type Triggered when transfer.transferring_stablecoinStablecoin transfer is in progress transfer.successTransfer completed successfully transfer.failedTransfer failed
Event type Triggered when customer.createdCustomer was created customer.under_verificationCustomer verification is in progress customer.approvedCustomer was approved customer.rfiCustomer requires additional information (can resubmit) customer.final_rejectionCustomer was permanently rejected
Event type Triggered when account.provisioningAccount is being provisioned and verified account.rfiAdditional information is required to continue verification account.activeAccount is verified and can receive funds account.closedAccount was permanently closed
Event type Triggered when destinations.under_verificationDestination verification is in progress destinations.approvedDestination was approved destinations.final_rejectionDestination was permanently rejected
Payload examples
On-ramp
Off-ramp
Transfer
Customer
Account
Destination
awaiting_funds
transferring_fiat
trading
transferring_stablecoin
success
failed
expired
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "onramp.awaiting_funds" ,
"data" : {
"id" : "123e4567-e89b-12d3-a456-426614174000" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"type" : "ON_RAMP" ,
"request" : {
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"rail" : "PIX" ,
"sourceCurrency" : "BRL" ,
"sourceAmount" : "10000.00" ,
"targetCurrency" : "USDC" ,
"purpose" : "PERSONAL_ACCOUNT"
},
"state" : {
"status" : "AWAITING_FUNDS" ,
"payment" : {
"rail" : "PIX" ,
"brCode" : "00020126580014br.gov.bcb.pix0136123e4567-e89b-12d3-a456-4266141740005204000053039865802BR5910Lumx Test6009Sao Paulo62070503***6304ABCD"
}
},
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "onramp.transferring_fiat" ,
"data" : {
"id" : "123e4567-e89b-12d3-a456-426614174000" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"type" : "ON_RAMP" ,
"request" : {
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"rail" : "PIX" ,
"sourceCurrency" : "BRL" ,
"sourceAmount" : "10000.00" ,
"targetCurrency" : "USDC" ,
"purpose" : "PERSONAL_ACCOUNT"
},
"state" : {
"status" : "TRANSFERRING_FIAT" ,
"payment" : {
"rail" : "PIX" ,
"brCode" : "00020126580014br.gov.bcb.pix0136123e4567-e89b-12d3-a456-4266141740005204000053039865802BR5910Lumx Test6009Sao Paulo62070503***6304ABCD"
}
},
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "onramp.trading" ,
"data" : {
"id" : "123e4567-e89b-12d3-a456-426614174000" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"type" : "ON_RAMP" ,
"request" : {
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"rail" : "PIX" ,
"sourceCurrency" : "BRL" ,
"sourceAmount" : "10000.00" ,
"targetCurrency" : "USDC" ,
"purpose" : "PERSONAL_ACCOUNT"
},
"state" : {
"status" : "TRADING" ,
"payment" : {
"rail" : "PIX" ,
"brCode" : "00020126580014br.gov.bcb.pix0136123e4567-e89b-12d3-a456-4266141740005204000053039865802BR5910Lumx Test6009Sao Paulo62070503***6304ABCD"
}
},
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "onramp.transferring_stablecoin" ,
"data" : {
"id" : "123e4567-e89b-12d3-a456-426614174000" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"type" : "ON_RAMP" ,
"request" : {
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"rail" : "PIX" ,
"sourceCurrency" : "BRL" ,
"sourceAmount" : "10000.00" ,
"targetCurrency" : "USDC" ,
"purpose" : "PERSONAL_ACCOUNT"
},
"state" : {
"status" : "TRANSFERRING_STABLECOIN" ,
"payment" : {
"rail" : "PIX" ,
"brCode" : "00020126580014br.gov.bcb.pix0136123e4567-e89b-12d3-a456-4266141740005204000053039865802BR5910Lumx Test6009Sao Paulo62070503***6304ABCD"
}
},
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "onramp.success" ,
"data" : {
"id" : "123e4567-e89b-12d3-a456-426614174000" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"type" : "ON_RAMP" ,
"request" : {
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"rail" : "PIX" ,
"sourceCurrency" : "BRL" ,
"sourceAmount" : "10000.00" ,
"targetCurrency" : "USDC" ,
"purpose" : "PERSONAL_ACCOUNT"
},
"state" : {
"status" : "SUCCESS" ,
"payment" : {
"rail" : "PIX" ,
"brCode" : "00020126580014br.gov.bcb.pix0136123e4567-e89b-12d3-a456-4266141740005204000053039865802BR5910Lumx Test6009Sao Paulo62070503***6304ABCD"
},
"receipt" : {
"sourceCurrency" : "BRL" ,
"sourceAmount" : "10000.00" ,
"targetCurrency" : "USDC" ,
"baseExchangeRate" : "5.25" ,
"baseTargetAmount" : "1904.76" ,
"fees" : {
"lumx" : {
"rate" : "0.005" ,
"flatAmount" : "0.00" ,
"totalAmount" : "9.52" ,
"currency" : "USDC"
},
"partner" : {
"rate" : "0.002" ,
"flatAmount" : "0.00" ,
"totalAmount" : "3.81" ,
"currency" : "USDC"
}
},
"finalTargetAmount" : "1891.43" ,
"finalExchangeRate" : "5.29" ,
"transactionHash" : "0xabc123def456abc123def456abc123def456abc123def456abc123def456abc1" ,
"blockExplorerUrl" : "https://polygonscan.com/tx/0xabc123def456abc123def456abc123def456abc123def456abc123def456abc1"
}
},
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "onramp.failed" ,
"data" : {
"id" : "123e4567-e89b-12d3-a456-426614174000" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"type" : "ON_RAMP" ,
"request" : {
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"rail" : "PIX" ,
"sourceCurrency" : "BRL" ,
"sourceAmount" : "10000.00" ,
"targetCurrency" : "USDC" ,
"purpose" : "PERSONAL_ACCOUNT"
},
"state" : {
"status" : "FAILED" ,
"payment" : {
"rail" : "PIX" ,
"brCode" : "00020126580014br.gov.bcb.pix0136123e4567-e89b-12d3-a456-4266141740005204000053039865802BR5910Lumx Test6009Sao Paulo62070503***6304ABCD"
},
"receipt" : {
"error" : {
"message" : "Your transaction has failed. Please contact support."
}
}
},
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "onramp.expired" ,
"data" : {
"id" : "123e4567-e89b-12d3-a456-426614174000" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"type" : "ON_RAMP" ,
"request" : {
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"rail" : "PIX" ,
"sourceCurrency" : "BRL" ,
"sourceAmount" : "10000.00" ,
"targetCurrency" : "USDC" ,
"purpose" : "PERSONAL_ACCOUNT"
},
"state" : {
"status" : "EXPIRED" ,
"payment" : {
"rail" : "PIX" ,
"brCode" : "00020126580014br.gov.bcb.pix0136123e4567-e89b-12d3-a456-4266141740005204000053039865802BR5910Lumx Test6009Sao Paulo62070503***6304ABCD"
}
},
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
transferring_stablecoin
trading
transferring_fiat
success
failed
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "offramp.transferring_stablecoin" ,
"data" : {
"id" : "123e4567-e89b-12d3-a456-426614174000" ,
"destinationId" : "ba123456-e89b-12d3-a456-426614174000" ,
"type" : "OFF_RAMP" ,
"request" : {
"destinationId" : "e80d3137-eddd-4791-b9b8-6e36b289f284" ,
"sourceCurrency" : "USDC" ,
"sourceAmount" : "1000.00" ,
"purpose" : "PERSONAL_ACCOUNT"
},
"state" : {
"status" : "TRANSFERRING_STABLECOIN"
},
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "offramp.trading" ,
"data" : {
"id" : "123e4567-e89b-12d3-a456-426614174000" ,
"destinationId" : "ba123456-e89b-12d3-a456-426614174000" ,
"type" : "OFF_RAMP" ,
"request" : {
"destinationId" : "e80d3137-eddd-4791-b9b8-6e36b289f284" ,
"sourceCurrency" : "USDC" ,
"sourceAmount" : "1000.00" ,
"purpose" : "PERSONAL_ACCOUNT"
},
"state" : {
"status" : "TRADING"
},
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "offramp.transferring_fiat" ,
"data" : {
"id" : "123e4567-e89b-12d3-a456-426614174000" ,
"destinationId" : "ba123456-e89b-12d3-a456-426614174000" ,
"type" : "OFF_RAMP" ,
"request" : {
"destinationId" : "e80d3137-eddd-4791-b9b8-6e36b289f284" ,
"sourceCurrency" : "USDC" ,
"sourceAmount" : "1000.00" ,
"purpose" : "PERSONAL_ACCOUNT"
},
"state" : {
"status" : "TRANSFERRING_FIAT"
},
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "offramp.success" ,
"data" : {
"id" : "123e4567-e89b-12d3-a456-426614174000" ,
"destinationId" : "ba123456-e89b-12d3-a456-426614174000" ,
"type" : "OFF_RAMP" ,
"request" : {
"destinationId" : "e80d3137-eddd-4791-b9b8-6e36b289f284" ,
"sourceCurrency" : "USDC" ,
"sourceAmount" : "1000.00" ,
"purpose" : "PERSONAL_ACCOUNT"
},
"state" : {
"status" : "SUCCESS" ,
"receipt" : {
"sourceCurrency" : "USDC" ,
"sourceAmount" : "1904.76" ,
"targetCurrency" : "BRL" ,
"baseExchangeRate" : "5.25" ,
"baseTargetAmount" : "10000.00" ,
"fees" : {
"lumx" : {
"rate" : "0.005" ,
"flatAmount" : "0.00" ,
"totalAmount" : "9.52" ,
"currency" : "USDC"
},
"partner" : {
"rate" : "0.002" ,
"flatAmount" : "0.00" ,
"totalAmount" : "3.81" ,
"currency" : "USDC"
}
},
"finalTargetAmount" : "9900.00" ,
"finalExchangeRate" : "5.20" ,
"transactionHash" : "0xabc123def456abc123def456abc123def456abc123def456abc123def456abc1" ,
"blockExplorerUrl" : "https://polygonscan.com/tx/0xabc123def456abc123def456abc123def456abc123def456abc123def456abc1"
}
},
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "offramp.failed" ,
"data" : {
"id" : "123e4567-e89b-12d3-a456-426614174000" ,
"destinationId" : "ba123456-e89b-12d3-a456-426614174000" ,
"type" : "OFF_RAMP" ,
"request" : {
"destinationId" : "e80d3137-eddd-4791-b9b8-6e36b289f284" ,
"sourceCurrency" : "USDC" ,
"sourceAmount" : "1000.00" ,
"purpose" : "PERSONAL_ACCOUNT"
},
"state" : {
"status" : "FAILED" ,
"receipt" : {
"error" : {
"message" : "Your transaction has failed. Please contact support."
}
}
},
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
transferring_stablecoin
success
failed
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "transfer.transferring_stablecoin" ,
"data" : {
"id" : "123e4567-e89b-12d3-a456-426614174000" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"type" : "TRANSFER" ,
"request" : {
"currency" : "USDC" ,
"from" : "123e4567-e89b-12d3-a456-426614174001" ,
"to" : "123e4567-e89b-12d3-a456-426614174002" ,
"amount" : "1000.000000"
},
"state" : {
"status" : "TRANSFERRING_STABLECOIN"
},
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "transfer.success" ,
"data" : {
"id" : "123e4567-e89b-12d3-a456-426614174000" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"type" : "TRANSFER" ,
"request" : {
"currency" : "USDC" ,
"from" : "123e4567-e89b-12d3-a456-426614174001" ,
"to" : "123e4567-e89b-12d3-a456-426614174002" ,
"amount" : "1000.000000"
},
"state" : {
"status" : "SUCCESS" ,
"receipt" : {
"transactionHash" : "0xabc123def456abc123def456abc123def456abc123def456abc123def456abc1" ,
"blockExplorerUrl" : "https://polygonscan.com/tx/0xabc123def456abc123def456abc123def456abc123def456abc123def456abc1"
}
},
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "transfer.failed" ,
"data" : {
"id" : "123e4567-e89b-12d3-a456-426614174000" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"type" : "TRANSFER" ,
"request" : {
"currency" : "USDC" ,
"from" : "123e4567-e89b-12d3-a456-426614174001" ,
"to" : "123e4567-e89b-12d3-a456-426614174002" ,
"amount" : "1000.000000"
},
"state" : {
"status" : "FAILED" ,
"receipt" : {
"error" : {
"message" : "Your transaction has failed. Please contact support."
}
}
},
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
created
under_verification
approved
rfi
final_rejection
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "customer.created" ,
"data" : {
"id" : "7f3e1a2b-4c5d-6e7f-8a9b-0c1d2e3f4a5b" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"status" : "created" ,
"level" : "BASIC" ,
"associatedParties" : [
{
"id" : "f0e9d8c7-b6a5-4f3e-2d1c-0b9a8f7e6d5c" ,
"associatedPartyId" : "c1d2e3f4-a5b6-7c8d-9e0f-a1b2c3d4e5f6" ,
"roles" : [ "SHAREHOLDER" ],
"status" : "approved" ,
"level" : "BASIC"
}
],
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "customer.under_verification" ,
"data" : {
"id" : "7f3e1a2b-4c5d-6e7f-8a9b-0c1d2e3f4a5b" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"status" : "under_verification" ,
"level" : "BASIC" ,
"associatedParties" : [
{
"id" : "f0e9d8c7-b6a5-4f3e-2d1c-0b9a8f7e6d5c" ,
"associatedPartyId" : "c1d2e3f4-a5b6-7c8d-9e0f-a1b2c3d4e5f6" ,
"roles" : [ "SHAREHOLDER" ],
"status" : "approved" ,
"level" : "BASIC"
}
],
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "customer.approved" ,
"data" : {
"id" : "7f3e1a2b-4c5d-6e7f-8a9b-0c1d2e3f4a5b" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"status" : "approved" ,
"level" : "BASIC" ,
"associatedParties" : [
{
"id" : "f0e9d8c7-b6a5-4f3e-2d1c-0b9a8f7e6d5c" ,
"associatedPartyId" : "c1d2e3f4-a5b6-7c8d-9e0f-a1b2c3d4e5f6" ,
"roles" : [ "SHAREHOLDER" ],
"status" : "approved" ,
"level" : "BASIC"
}
],
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "customer.rfi" ,
"data" : {
"id" : "7f3e1a2b-4c5d-6e7f-8a9b-0c1d2e3f4a5b" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"status" : "rfi" ,
"level" : "BASIC" ,
"statusReason" : {
"rejectLabels" : [ "DOCUMENT_QUALITY" ],
"rejectSubLabels" : [ "DOCUMENT_BLURRY" ],
"documents" : [
{
"type" : "PASSPORT" ,
"status" : "DECLINED" ,
"comment" : "Document is blurry and illegible" ,
"rejectLabels" : [ "DOCUMENT_QUALITY" ]
}
]
},
"associatedParties" : [
{
"id" : "f0e9d8c7-b6a5-4f3e-2d1c-0b9a8f7e6d5c" ,
"associatedPartyId" : "c1d2e3f4-a5b6-7c8d-9e0f-a1b2c3d4e5f6" ,
"roles" : [ "SHAREHOLDER" ],
"status" : "rfi" ,
"level" : "BASIC" ,
"statusReason" : {
"rejectLabels" : [ "DOCUMENT_QUALITY" ],
"rejectSubLabels" : [ "DOCUMENT_BLURRY" ]
}
}
],
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "customer.final_rejection" ,
"data" : {
"id" : "7f3e1a2b-4c5d-6e7f-8a9b-0c1d2e3f4a5b" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"status" : "final_rejection" ,
"level" : "BASIC" ,
"statusReason" : {
"rejectLabels" : [ "FRAUDULENT_PATTERNS" ],
"rejectSubLabels" : [ "MULTIPLE_SUBMISSIONS" ]
},
"associatedParties" : [
{
"id" : "f0e9d8c7-b6a5-4f3e-2d1c-0b9a8f7e6d5c" ,
"associatedPartyId" : "c1d2e3f4-a5b6-7c8d-9e0f-a1b2c3d4e5f6" ,
"roles" : [ "SHAREHOLDER" ],
"status" : "rfi" ,
"level" : "BASIC" ,
"statusReason" : {
"rejectLabels" : [ "DOCUMENT_QUALITY" ],
"rejectSubLabels" : [ "DOCUMENT_BLURRY" ]
}
}
],
"metadata" : {},
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
provisioning
rfi
active
closed
{
"eventId" : "d26c479f-f786-4be8-a694-c4da0e8393e7" ,
"eventType" : "account.provisioning" ,
"data" : {
"id" : "ce4c89f3-60f2-45a3-9de7-415a462dd95c" ,
"customerId" : "96fec1e2-78d9-4978-813d-674bba64020d" ,
"status" : "PROVISIONING" ,
"currency" : "USD" ,
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "account.rfi" ,
"data" : {
"id" : "ce4c89f3-60f2-45a3-9de7-415a462dd95c" ,
"customerId" : "96fec1e2-78d9-4978-813d-674bba64020d" ,
"status" : "RFI" ,
"currency" : "USD" ,
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "81d783f1-3675-4f3a-92d1-e4f5af66cca3" ,
"eventType" : "account.active" ,
"data" : {
"id" : "d06d342c-629a-4d99-a26f-34e8ea528403" ,
"customerId" : "768d6aac-627e-44e8-aabd-d51d10f59c02" ,
"status" : "ACTIVE" ,
"currency" : "EUR" ,
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "account.closed" ,
"data" : {
"id" : "d06d342c-629a-4d99-a26f-34e8ea528403" ,
"customerId" : "768d6aac-627e-44e8-aabd-d51d10f59c02" ,
"status" : "CLOSED" ,
"currency" : "EUR" ,
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
under_verification
approved
final_rejection
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "destinations.under_verification" ,
"data" : {
"id" : "ba123456-e89b-12d3-a456-426614174000" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"status" : "UNDER_VERIFICATION" ,
"level" : "BASIC" ,
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "destinations.approved" ,
"data" : {
"id" : "ba123456-e89b-12d3-a456-426614174000" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"status" : "APPROVED" ,
"level" : "BASIC" ,
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
{
"eventId" : "550e8400-e29b-41d4-a716-446655440000" ,
"eventType" : "destinations.final_rejection" ,
"data" : {
"id" : "ba123456-e89b-12d3-a456-426614174000" ,
"customerId" : "3c90c3cc-0d44-4b50-8888-8dd25736052a" ,
"status" : "FINAL_REJECTION" ,
"level" : "BASIC" ,
"createdAt" : "2024-03-20T15:30:00Z" ,
"updatedAt" : "2024-03-20T15:30:05Z"
}
}
Verifying webhook signatures
Each webhook request includes three headers for signature verification:
Header Description webhook-idUnique message identifier webhook-timestampUnix timestamp (seconds) of when the message was sent webhook-signatureBase64-encoded signature(s), space-delimited and prefixed with version
To verify a webhook signature, you construct the signed content, compute the expected signature, and compare it with the header value.
The signed content is created by concatenating the webhook-id, webhook-timestamp, and request body, separated by dots:
signed_content = "${webhook_id}.${webhook_timestamp}.${body}"
The signature is computed as a HMAC-SHA256 hash of the signed content using your webhook secret (base64-decoded) as the key, then base64-encoded.
Your webhook signing secret starts with whsec_. You must strip this prefix and base64-decode the remainder before using it as the HMAC key.
When you rotate your signing secret, Lumx continues signing messages with both the old and new secrets for 24 hours. This means the webhook-signature header may contain multiple signatures (e.g., v1,<old> v1,<new>). Your verification code should accept any valid signature from the list, which the examples below already handle.
TypeScript
JavaScript
Python
Go
import { createHmac , timingSafeEqual } from "crypto" ;
function verifyWebhook (
body : string ,
headers : Record < string , string >,
secret : string
) : boolean {
const msgId = headers [ "webhook-id" ];
const timestamp = headers [ "webhook-timestamp" ];
const signature = headers [ "webhook-signature" ];
if ( ! msgId || ! timestamp || ! signature ) {
return false ;
}
// Reject messages older than 5 minutes to prevent replay attacks
const now = Math . floor ( Date . now () / 1000 );
if ( Math . abs ( now - parseInt ( timestamp )) > 300 ) {
return false ;
}
// Strip the whsec_ prefix and decode the secret
const secretBytes = Buffer . from ( secret . replace ( "whsec_" , "" ), "base64" );
// Compute the expected signature
const signedContent = ` ${ msgId } . ${ timestamp } . ${ body } ` ;
const expectedSignature = createHmac ( "sha256" , secretBytes )
. update ( signedContent )
. digest ( "base64" );
// Compare against all provided signatures (there may be multiple)
const signaturesV1 = signature
. split ( " " )
. filter (( s ) => s . startsWith ( "v1," ))
. map (( s ) => s . substring ( 3 ));
return signaturesV1 . some (( sig ) =>
timingSafeEqual ( Buffer . from ( sig ), Buffer . from ( expectedSignature ))
);
}
const { createHmac , timingSafeEqual } = require ( "crypto" );
function verifyWebhook ( body , headers , secret ) {
const msgId = headers [ "webhook-id" ];
const timestamp = headers [ "webhook-timestamp" ];
const signature = headers [ "webhook-signature" ];
if ( ! msgId || ! timestamp || ! signature ) {
return false ;
}
// Reject messages older than 5 minutes to prevent replay attacks
const now = Math . floor ( Date . now () / 1000 );
if ( Math . abs ( now - parseInt ( timestamp )) > 300 ) {
return false ;
}
// Strip the whsec_ prefix and decode the secret
const secretBytes = Buffer . from ( secret . replace ( "whsec_" , "" ), "base64" );
// Compute the expected signature
const signedContent = ` ${ msgId } . ${ timestamp } . ${ body } ` ;
const expectedSignature = createHmac ( "sha256" , secretBytes )
. update ( signedContent )
. digest ( "base64" );
// Compare against all provided signatures (there may be multiple)
const signaturesV1 = signature
. split ( " " )
. filter (( s ) => s . startsWith ( "v1," ))
. map (( s ) => s . substring ( 3 ));
return signaturesV1 . some (( sig ) =>
timingSafeEqual ( Buffer . from ( sig ), Buffer . from ( expectedSignature ))
);
}
import base64
import hashlib
import hmac
import time
def verify_webhook (
body : str , headers : dict[ str , str ], secret : str
) -> bool :
msg_id = headers.get( "webhook-id" )
timestamp = headers.get( "webhook-timestamp" )
signature = headers.get( "webhook-signature" )
if not msg_id or not timestamp or not signature:
return False
# Reject messages older than 5 minutes to prevent replay attacks
now = int (time.time())
if abs (now - int (timestamp)) > 300 :
return False
# Strip the whsec_ prefix and decode the secret
secret_bytes = base64.b64decode(secret.removeprefix( "whsec_" ))
# Compute the expected signature
signed_content = f " { msg_id } . { timestamp } . { body } "
expected_signature = base64.b64encode(
hmac.new(
secret_bytes, signed_content.encode(), hashlib.sha256
).digest()
).decode()
# Compare against all provided signatures (there may be multiple)
signatures_v1 = [
s.removeprefix( "v1," )
for s in signature.split( " " )
if s.startswith( "v1," )
]
return any (
hmac.compare_digest(sig, expected_signature)
for sig in signatures_v1
)
package webhook
import (
" crypto/hmac "
" crypto/sha256 "
" encoding/base64 "
" fmt "
" math "
" net/http "
" strconv "
" strings "
" time "
)
func VerifyWebhook ( body string , headers http . Header , secret string ) bool {
msgID := headers . Get ( "webhook-id" )
timestamp := headers . Get ( "webhook-timestamp" )
signature := headers . Get ( "webhook-signature" )
if msgID == "" || timestamp == "" || signature == "" {
return false
}
// Reject messages older than 5 minutes to prevent replay attacks
ts , err := strconv . ParseInt ( timestamp , 10 , 64 )
if err != nil {
return false
}
now := time . Now (). Unix ()
if math . Abs ( float64 ( now - ts )) > 300 {
return false
}
// Strip the whsec_ prefix and decode the secret
secretBytes , err := base64 . StdEncoding . DecodeString (
strings . TrimPrefix ( secret , "whsec_" ),
)
if err != nil {
return false
}
// Compute the expected signature
signedContent := fmt . Sprintf ( " %s . %s . %s " , msgID , timestamp , body )
mac := hmac . New ( sha256 . New , secretBytes )
mac . Write ([] byte ( signedContent ))
expectedSignature := base64 . StdEncoding . EncodeToString ( mac . Sum ( nil ))
// Compare against all provided signatures (there may be multiple)
for _ , sig := range strings . Split ( signature , " " ) {
if strings . HasPrefix ( sig , "v1," ) {
if hmac . Equal (
[] byte ( strings . TrimPrefix ( sig , "v1," )),
[] byte ( expectedSignature ),
) {
return true
}
}
}
return false
}
Retries
If your endpoint does not return a 2xx response, Lumx retries the delivery with exponential backoff:
Attempt Delay 1 Immediately 2 5 seconds 3 5 minutes 4 30 minutes 5 2 hours 6 5 hours 7 10 hours 8 10 hours
After all retry attempts are exhausted, Lumx marks the message as failed. You can manually retry failed messages from the Dashboard .
Use the webhook-id header to deduplicate events in case your endpoint receives the same event more than once.
IP allowlisting
If your infrastructure requires allowlisting specific IPs, add the following addresses. These IPs are shared across both sandbox and production environments:
44.214.29.156/32
3.82.0.0/32
100.56.2.161/32
Basic authentication
If your endpoint requires HTTP Basic authentication, include the credentials directly in the endpoint URL:
https://username:password@your-domain.com/webhook
Lumx extracts the credentials and sends them in the Authorization header on every webhook request:
Authorization: Basic <base64(username:password)>