Hooks
Define custom hooks to execute on survey and message events. All hooks are optional.
Available hooksโ
| Hook Name | init | surveyStart | messageStart |
|---|---|---|---|
onReady | โ | โ | โ |
onSurveyDisplayAllowed | โ | โ | โ |
onMessageDisplayAllowed | โ | โ | โ |
onRecordingStarted | โ | โ | โ |
onRecordingStopped | โ | โ | โ |
onSurveyShowed | โ | โ | โ |
onSurveyStarted | โ | โ | โ |
onQuestionReplied | โ | โ | โ |
onButtonNavigateStarted | โ | โ | โ |
onButtonNavigateCompleted | โ | โ | โ |
onSurveyCompleted | โ | โ | โ |
onSurveyHidden | โ | โ | โ |
onMessageShowed | โ | โ | โ |
onMessageStarted | โ | โ | โ |
onMessageCompleted | โ | โ | โ |
onMessageHidden | โ | โ | โ |
Payload structureโ
Hook payloads follow the shared browser SDK types:
type Channel = {
id: string;
type: "widget" | "ios" | "android";
};
type User = {
anonymous_id: string;
userId?: string;
};
type Survey = {
id: string;
survey_position:
| "center-left"
| "center-center"
| "center-right"
| "bottom-left"
| "bottom-center"
| "bottom-right";
survey_format: "conversationnal" | "cards";
survey_size: 100;
};
type Message = {
id: string;
messagey_size: 100;
};
type ResponseStatus =
| "displayed"
| "started"
| "ended"
| "closed"
| "interrupted";
type ResponseItemQuestion = {
id: string;
title: string;
type: "text" | "video";
url?: string;
};
type ResponseItemAnswer = {
replied_at?: string;
fields?: {
type: "string" | "number" | "boolean" | "none" | "time" | "file";
text?: string;
number?: number;
boolean?: boolean;
time?: string;
button_url?: string;
button_url_target?: "in-page" | "in-page-spa" | "new-tab";
button_actions?: object[];
}[];
};
type ResponseItem = {
question: ResponseItemQuestion;
answer: ResponseItemAnswer;
};
Decision hooks (onSurveyDisplayAllowed, onMessageDisplayAllowed) must answer within 5 seconds. Past that deadline the display evaluation fails and the survey is not shown โ keep these callbacks fast and avoid slow network calls.
Exact callback payloads:
onReady(payload: { channel: Channel; user: User }): void;
onRecordingStarted(payload: { channel: Channel; user: User }): void;
onRecordingStopped(payload: { channel: Channel; user: User }): void;
onSurveyDisplayAllowed(payload: {
channel: Channel;
user: User;
survey: Survey;
}): boolean;
onMessageDisplayAllowed(payload: {
channel: Channel;
user: User;
survey: Survey;
}): boolean;
onSurveyShowed(payload: {
channel: Channel;
user: User;
survey: Survey;
response: {
id: string;
items: ResponseItem[];
};
}): void;
onSurveyStarted(payload: {
channel: Channel;
user: User;
survey: Survey;
response: {
id: string;
};
}): void;
onSurveyCompleted(payload: {
channel: Channel;
user: User;
survey: Survey;
response: {
id: string;
items: ResponseItem[];
};
}): void;
onSurveyHidden(payload: {
channel: Channel;
user: User;
survey: Survey;
response: {
id: string;
items: ResponseItem[];
hide_reason: ResponseStatus;
};
}): void;
onQuestionReplied(payload: {
channel: Channel;
user: User;
survey: Survey;
response: {
id: string;
status: null;
question: ResponseItemQuestion;
answer: ResponseItemAnswer;
items: ResponseItem[];
};
}): void;
onButtonNavigateStarted(payload: {
channel: Channel;
user: User;
survey: Survey;
response: {
id: string;
};
button: {
url: string;
url_target: "in-page" | "in-page-spa" | "new-tab";
};
}): void;
onButtonNavigateCompleted(payload: {
channel: Channel;
user: User;
survey: Survey;
response: {
id: string;
};
button: {
url: string;
url_target: "in-page" | "in-page-spa" | "new-tab";
};
}): void;
onMessageShowed(payload: {
channel: Channel;
user: User;
message: Message;
response: {
id: string;
items: ResponseItem[];
};
}): void;
onMessageStarted(payload: {
channel: Channel;
user: User;
message: Message;
response: {
id: string;
};
}): void;
onMessageCompleted(payload: {
channel: Channel;
user: User;
message: Message;
response: {
id: string;
items: ResponseItem[];
};
}): void;
onMessageHidden(payload: {
channel: Channel;
user: User;
message: Message;
response: {
id: string;
items: ResponseItem[];
hide_reason: ResponseStatus;
};
}): void;
On module initโ
ScreebModule.forRoot({
autoInit: true,
websiteId: "<YOUR-CHANNEL-ID>",
hooks: {
version: "1.0.0",
onReady: (payload) => {
console.log("Screeb ready", payload);
},
onSurveyShowed: (payload) => {
console.log("Survey shown", payload);
},
onSurveyCompleted: (payload) => {
console.log("Survey completed", payload);
},
},
});
On surveyStartโ
import { ScreebModule, ScreebService } from "@screeb/sdk-angular";
constructor(private screeb: ScreebService) {}
await this.screeb.surveyStart("<SURVEY_ID>", "", true, {}, {
onSurveyCompleted: (payload) => {
console.log("Survey completed", payload);
},
});
On messageStartโ
await this.screeb.messageStart("<MESSAGE_ID>", true, {}, {
onMessageHidden: (payload) => {
console.log("Message hidden", payload);
},
});
For complete hook payload definitions, see the JS tag hooks reference.
SPA navigation (in-page-spa)โ
The in-page-spa button target navigates without a full page reload, so the SDK and the onButtonNavigateCompleted hook survive. It auto-detects the host router: click-interception routers (SvelteKit, Astro, Nuxt) are driven via a real anchor click, and History-API routers (React Router, Angular, Vue) fall back to history.pushState + popstate (with hashchange for hash-mode routers). Cross-origin targets always trigger a normal full navigation.
For a router that can't be auto-detected, register a spaNavigationHandler in ScreebModule.forRoot():
ScreebModule.forRoot({
autoInit: true,
websiteId: "<YOUR-CHANNEL-ID>",
spaNavigationHandler: (url) => router.navigateByUrl(url), // your Angular Router
});
See the JS tag hooks reference for details.