Skip to main content

Events

The widget communicates with your host app only via callbacks you pass in the config. There's no shared DOM state to poll, no postMessage to intercept, no global bus. If you want to know what the widget is doing, listen to its events.

onReady

Fires once, after the widget has mounted, fetched initial resources (sponsor config, provider products), and rendered the first step.

Signature: () => void

Use it to hide your app's loading spinner or to log a widget-ready metric.

onStepChange

Fires every time the widget moves to a new step. Purely informational — mostly useful for analytics or progress bars in your host app.

Signature: (step: { name, index, total }) => void

Step names (stable identifiers you can switch on):

  • search_beneficiary
  • select_products
  • capture_indication
  • review_and_submit
  • otp_challenge
  • outcome

onComplete

Fires once, when the beneficiary finishes the flow with an outcome (approved, rejected, or async-pending). After onComplete fires, no further step-changes are emitted.

Signature: (outcome: PreAuthOutcome) => void

Payload shape

type PreAuthOutcome = {
authorization_request_id: string;
status: 'COMPLETED' | 'PENDING_SPONSOR';
authorizations: Array<{
authorization_code: string;
product_type: 'MEDICATION' | 'LAB' | 'IMAGING' | 'PROCEDURE';
status: 'APPROVED' | 'REJECTED' | 'PENDING_SPONSOR';
rejection_reason?: string;
expires_at?: string; // ISO 8601
products: Array<{ product_code: string; quantity: number }>;
}>;
duration_ms: number; // Widget wall-clock time
telemetry_id?: string; // The trackingId you set in config
};
  • status: 'COMPLETED' — Sponsor evaluated synchronously. authorizations is populated with real outcomes.
  • status: 'PENDING_SPONSOR' — Sponsor took the request but is processing offline. authorizations may include placeholder entries with status: 'PENDING_SPONSOR'. Listen for the authorization.created webhook to get the final decision.

Handling the outcome

onComplete: (outcome) => {
if (outcome.status === 'PENDING_SPONSOR') {
showToast('Sponsor is processing your request — we\'ll notify you.');
persistPendingRequest(outcome.authorization_request_id);
return;
}
outcome.authorizations.forEach((auth) => {
if (auth.status === 'APPROVED') {
renderQrCode(auth.authorization_code);
} else {
showRejection(auth.rejection_reason);
}
});
}

onError

Fires on any unrecoverable error. The widget stays mounted (so the user can retry if it's a network error) but no further progress is possible without intervention.

Signature: (err: PreAuthError) => void

Payload shape

type PreAuthError = {
code: string; // Osigu error_code, e.g. '071-104'
message: string; // Human-readable
step: string; // Which step raised it (see onStepChange step names)
recoverable: boolean; // true if a retry is possible
cause?: unknown; // Underlying error (network, parse, etc.)
};

Common codes:

  • 071-104 — Product not registered against your provider.
  • 071-107 — Sponsor rejected the request outright (before individual authorization decisions).
  • 071-201 — OTP flow failed after 3 attempts.
  • WIDGET-NETWORK — HTTP failed after retries.
  • WIDGET-TOKENgetToken returned invalid data or the token is expired.

onClose

Fires when the user actively dismisses the widget (X button, ESC key, or clicks outside if you enabled backdrop dismissal). Does not fire on onComplete or onError.

Signature: () => void

Use it to release any UI state your host app was tracking (progress indicators, cancelled analytics events, etc.).

Ordering guarantees

  • onReady fires first, always.
  • onStepChange may fire multiple times, always after onReady.
  • Exactly one of onComplete / onError / onClose fires last.
  • Once the terminal callback has fired, no further callbacks will fire — even if the user interacts with the widget's DOM afterwards.