SDK Events Reference
The Coal widget communicates with your page through the browser's window.postMessage API. Both the JavaScript SDK's .on() method and React's onReady / onSuccess / onError / onCancel callbacks are built on top of these events.
This page documents every event type, its payload shape, and how to listen for events directly if you are not using an SDK.
Event Overview
| Event | When it fires |
|---|---|
coal:ready | Widget iframe is fully loaded and ready for user interaction |
coal:success | Payment confirmed by Coal's verification service |
coal:error | Payment failed, was rejected, or encountered an error |
coal:cancel | User dismissed the widget without completing payment |
coal:close | Treated the same as coal:cancel |
Event Payloads
coal:ready
Fired when the widget has mounted and the user can begin the payment flow.
1interface CoalReadyEvent {2 type: 'coal:ready';3 sessionId: string;4}
Example payload:
1{2 "type": "coal:ready",3 "sessionId": "cm9x4k2j00003lb08n5qz7v1r"4}
coal:success
Fired after Coal confirms the transfer. This is the event you should use to unlock content, fulfill orders, or redirect the user.
1interface CoalPaymentSuccessEvent {2 type: 'coal:success';3 sessionId: string;4 txHash: string;5}
Example payload:
1{2 "type": "coal:success",3 "sessionId": "cm9x4k2j00003lb08n5qz7v1r",4 "txHash": "0x3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b"5}
Important:
coal:successmeans Coal has verified the transaction on Base. For critical business logic (order fulfillment, access grants), always corroborate by also listening to thecheckout.session.completedwebhook from your server. Client-side events can be spoofed; webhooks cannot.
coal:error
Fired when the payment cannot be completed. This includes wallet rejections, insufficient balance, network failures, and blocked addresses.
1interface CoalPaymentErrorEvent {2 type: 'coal:error';3 message: string;4}
Example payload:
1{2 "type": "coal:error",3 "message": "Your wallet does not have enough of the configured settlement token to complete this payment."4}
coal:cancel
Fired when the user closes the widget without completing payment (e.g., clicking an X button or pressing Escape). No payload fields beyond type.
1interface CoalPaymentCancelEvent {2 type: 'coal:cancel';3 sessionId: string;4}
Example payload:
1{2 "type": "coal:cancel",3 "sessionId": "cm9x4k2j00003lb08n5qz7v1r"4}
A cancelled session is not reusable — create a new session if the user wants to try again.
Listening via SDK
JavaScript SDK
1const instance = CoalCheckout.mount('#coal-widget', { sessionId });23instance.on('ready', (data) => {4 console.log('Widget ready for session:', data.sessionId);5});67instance.on('success', (data) => {8 console.log('Paid!', data.txHash);9 window.location.href = '/success?session_id=' + data.sessionId;10});1112instance.on('error', (data) => {13 console.error(data.message);14 showErrorToast(data.message);15 instance.unmount();16});1718instance.on('cancel', () => {19 instance.unmount();20 document.getElementById('coal-widget').style.display = 'none';21});
React SDK
1import { CoalWidget } from '@coal/react';23<CoalWidget4 sessionId={sessionId}5 onSuccess={(data) => {6 // data: { sessionId, txHash }7 router.push(`/success?session_id=${data.sessionId}`);8 }}9 onError={(data) => {10 setError(data.message);11 }}12 onCancel={() => {13 setShowWidget(false);14 }}15/>
Listening Directly via window.addEventListener
If you are not using a Coal SDK (e.g., you are embedding the iframe manually), you can listen to raw postMessage events. Always validate the origin before processing.
1window.addEventListener('message', function handleCoalEvent(event) {2 // SECURITY: Only accept messages from the Coal widget origin3 if (event.origin !== 'https://usecoal.xyz') return;45 const { type, ...data } = event.data;67 switch (type) {8 case 'coal:ready':9 console.log('Widget ready:', data.sessionId);10 break;1112 case 'coal:success':13 console.log('Payment confirmed:', data.txHash);14 // Fulfill order, redirect, etc.15 window.removeEventListener('message', handleCoalEvent);16 break;1718 case 'coal:error':19 console.error('Payment error:', data.message);20 window.removeEventListener('message', handleCoalEvent);21 break;2223 case 'coal:cancel':24 case 'coal:close':25 console.log('User cancelled session:', data.sessionId);26 window.removeEventListener('message', handleCoalEvent);27 break;28 }29});
Always check
event.origin === 'https://usecoal.xyz'before processing. Failing to validate the origin would allow any page to send fake payment success messages to your handler.
TypeScript: Full Union Type
1export type CoalEvent =2 | { type: 'coal:ready'; sessionId: string }3 | { type: 'coal:success'; sessionId: string; txHash: string }4 | { type: 'coal:error'; message: string }5 | { type: 'coal:cancel'; sessionId: string };67window.addEventListener('message', (event: MessageEvent) => {8 if (event.origin !== 'https://usecoal.xyz') return;9 const coalEvent = event.data as CoalEvent;1011 if (coalEvent.type === 'coal:success') {12 // coalEvent is now narrowed to the success shape13 handleSuccess(coalEvent.txHash);14 }15});
