Skip to content

Models: in-app messages and surveys

In-app messages, carousels and surveys, from package:crm_sdk/wire.dart.

import 'package:crm_sdk/wire.dart';
NameWhat it is
AppLinksReportThe destinations a build declares, sent when a check-in asks (PUT /guest/v1/app-links): PurrCRM offers them to staff to add to the app’s catalogue.
CarouselScreens the guest moves through, which may branch: each button and each answer of a choice says where it goes (a screen, or the end), and a screen’s CarouselScreen.next says where Next and a forward swipe go.
CarouselBackgroundWhat is behind a screen.
CarouselButtonOne of a screen’s buttons.
CarouselChoiceA question on a screen, answered by tapping its options.
CarouselImageA screen’s hero picture.
CarouselOptionOne answer of a choice.
CarouselScreenOne screen of a carousel.
CarouselStyleA carousel’s look, or a screen’s over it.
CheckinWhat a check-in answers: the messages to show now, in order.
CheckinBodyWhat the app says when it checks in.
DeclaredLinkOne place in the host a link may go, as the host declares it (CrmHost.links): its name for staff, its path template (/offers/{offer_id}), and optionally its page on the web and its screen’s name.
ExportReceiptA message the guest was sent or shown, in their export.
ExportSurveyAnswerOne answer, as the question’s words and the answer’s.
ExportSurveyResponseThe guest’s answers to one survey, in their export.
ExportTestPhoneThe guest’s account being a test phone, in their export: staff’s name for it, and since when.
ExportTestSendOne test staff sent to the guest’s test phone, in their export: what it was, and what became of it.
InAppBannerA strip across the screen.
InAppMessageOne in-app message: exactly one of its contents, by kind.
InAppPostA full-screen message, from a teammate or from the product.
OpenMessageBodyThe message a link opened: the session it is shown in.
ReceiptBodyWhat the guest did with a message.
SurveyA survey: steps of questions, one after another, a branch skipping ahead on an answer, ending at a thank-you step or the last.
SurveyAnswerOne answer: a number, options, or words, by the question’s type.
SurveyAnswerBodyA survey’s answers so far.
SurveyBranchA step’s way forward on an answer: a number in [min, max], or a choice among values, goes to next.
SurveyOptionOne option of a choice question.
SurveyQuestionOne question.
SurveyStepOne step of a survey.
SurveySubmitA survey finished: every answer on the path the guest took.
carouselEndThe target that ends a carousel.
checkinEveryThe least time between two check-ins; a sooner one is answered throttled.
maxCarouselScreensThe most screens a carousel has.
maxLongAnswerRunesThe longest long answer.
maxOtherRunesThe longest words for “Other”.
maxReceiptPathThe most screens a receipt’s path names.
maxShortAnswerRunesThe longest short answer.
otherOptionThe option id of “Other” on a choice question that allows it.
class AppLinksReport

The destinations a build declares, sent when a check-in asks (PUT /guest/v1/app-links): PurrCRM offers them to staff to add to the app’s catalogue.

factory AppLinksReport.fromJson(Map<String, Object?> json)
const AppLinksReport({required String appVersion, required String digest, required List<DeclaredLink> links})
final String appVersion
final String digest

linksDigest of links.

final List<DeclaredLink> links
Map<String, Object?> toJson()
class Carousel

Screens the guest moves through, which may branch: each button and each answer of a choice says where it goes (a screen, or the end), and a screen’s CarouselScreen.next says where Next and a forward swipe go. Screens may loop back; every screen the guest can reach has a way to the end (PurrCRM checks it), and lib/src/logic/carousel.dart walks it.

How it looks is carousel_style.json’s numbers, picked by the words here and in each screen. A word this package does not know reads as unknown, which is drawn as the default; an optional part (a screen’s picture, its background, its choice, a style) that cannot be read is left out rather than losing the whole message.

factory Carousel.fromJson(Map<String, Object?> json)
const Carousel({required List<CarouselScreen> screens, String start = '', CarouselProgress progress = CarouselProgress.unknown, bool skip = false, CarouselPresentation presentation = CarouselPresentation.unknown, bool locked = false, CarouselStyle? style})
final bool locked

Left only through its own buttons: no close, no Skip, no drag down, no tap outside, and the system’s back goes back a screen at most.

final CarouselPresentation presentation

How it is shown; unknown is a sheet.

final CarouselProgress progress

What the top bar shows of how far the guest is; unknown is dots for one line of at most seven screens, and a bar otherwise.

final List<CarouselScreen> screens

One to fifty, in the editor’s order, which is also where a screen’s next goes when it names none.

final bool skip

A “Skip” in the top bar in place of the close button.

final String start

The id of the screen it opens on; empty for the first.

final CarouselStyle? style

The carousel’s look, which each screen’s own overrides field by field; null for the defaults.

class CarouselBackground

What is behind a screen. Words over a colour, a gradient or a picture are drawn white or near-black, whichever reads better, unless a block says otherwise.

factory CarouselBackground.fromJson(Map<String, Object?> json)
const CarouselBackground({required BackgroundKind kind, String color = '', String color2 = '', int angle = 0, String imageUrl = '', CarouselScrim scrim = CarouselScrim.unknown})
final int angle

A gradient’s direction in degrees, as CSS’s linear-gradient: 0 runs from the bottom up, 90 from left to right.

final String color

A colour’s, and a gradient’s first stop: a ColorToken’s word or #rrggbb.

final String color2

A gradient’s last stop.

final String imageUrl

An image background’s https address: a decoration, with no alt.

final BackgroundKind kind
final CarouselScrim scrim

Over an image; unknown is none.

class CarouselButton

One of a screen’s buttons.

factory CarouselButton.fromJson(Map<String, Object?> json)
const CarouselButton({required String id, required String label, required CarouselButtonAction action, ContentButtonStyle style = ContentButtonStyle.unknown, String screen = '', String link = '', CarouselPermission permission = CarouselPermission.unknown})
final CarouselButtonAction action

What it does (the wire’s do); an action this package does not know is unknown, and goes where the screen’s next goes.

final String id

What the receipts call it.

final String label
final String link

What link opens.

final CarouselPermission permission

What permission asks the phone for.

final String screen

Where screen goes, and where permission goes after asking (empty is the screen’s next): a screen’s id, or “end”.

final ContentButtonStyle style

Unknown is primary.

class CarouselChoice

A question on a screen, answered by tapping its options.

factory CarouselChoice.fromJson(Map<String, Object?> json)
const CarouselChoice({required List<CarouselOption> options, bool multiple = false})
final bool multiple

Several may be picked, then Continue goes to the screen’s next; a single choice goes on as soon as one is tapped, to its option’s next.

final List<CarouselOption> options

Two to eight.

class CarouselImage

A screen’s hero picture.

factory CarouselImage.fromJson(Map<String, Object?> json)
const CarouselImage({required String url, required String alt, ImageSize size = ImageSize.unknown, ImageAspect aspect = ImageAspect.unknown, ImageFit fit = ImageFit.unknown, ImageShape shape = ImageShape.unknown})
final String alt

What it shows, for a guest who cannot see it.

final ImageAspect aspect

Unknown is auto, cover and rounded.

final ImageFit fit
final ImageShape shape
final ImageSize size

How tall; unknown is medium (full for image_full).

final String url

An https address.

class CarouselOption

One answer of a choice.

factory CarouselOption.fromJson(Map<String, Object?> json)
const CarouselOption({required String id, required String label, String emoji = '', String next = ''})
final String emoji

Drawn before the label; empty for none.

final String id

What the receipts call it.

final String label
final String next

Where tapping it goes, in a single choice: a screen’s id, “end”, or empty for the screen’s next.

class CarouselScreen

One screen of a carousel.

factory CarouselScreen.fromJson(Map<String, Object?> json)
const CarouselScreen({required String id, required List<ContentBlock> blocks, CarouselLayout layout = CarouselLayout.unknown, CarouselImage? image, ContentAlign align = ContentAlign.unknown, ContentVAlign valign = ContentVAlign.unknown, CarouselBackground? background, List<CarouselButton> buttons = const <CarouselButton>[], CarouselChoice? choice, String next = '', CarouselStyle? style})
final ContentAlign align

Start or center; unknown is the layout’s.

final CarouselBackground? background

Null for the sheet’s own.

final List<ContentBlock> blocks
final List<CarouselButton> buttons

Up to three, stacked, the first the main one; none gives the screen the package’s Next, or Done where it ends.

final CarouselChoice? choice

A question, answered by tapping its options; null for none.

final String id

What the targets and the receipts call it.

final CarouselImage? image

The hero picture: above the words (image_top), behind them (image_full), a smaller one above them (centred).

final CarouselLayout layout

Unknown is centred.

final String next

Where Next goes: a screen’s id, “end”, or empty for the screen after this one (the end after the last).

final CarouselStyle? style

Overrides the carousel’s style, field by field.

final ContentVAlign valign

Unknown is the layout’s.

class CarouselStyle

A carousel’s look, or a screen’s over it. Every field is optional: unknown (or empty) is the default, and on a screen, the carousel’s.

factory CarouselStyle.fromJson(Map<String, Object?> json)
const CarouselStyle({String accent = '', CarouselCorner corner = CarouselCorner.unknown, CarouselButtonShape buttonShape = CarouselButtonShape.unknown, CarouselTextScale textScale = CarouselTextScale.unknown})
final String accent

The buttons’, the progress’s and the ticks’ colour: a ColorToken’s word or #rrggbb; empty for the app’s brand.

final CarouselButtonShape buttonShape
final CarouselCorner corner
final CarouselTextScale textScale
CarouselStyle over(CarouselStyle? base)

This style over base: each field this one sets, and base’s where it sets none.

class Checkin

What a check-in answers: the messages to show now, in order.

factory Checkin.fromJson(Map<String, Object?> json)
const Checkin({List<InAppMessage> messages = const <InAppMessage>[], int unread = 0, int nextCheckinSeconds = 20, bool throttled = false, bool testPhone = false, int unreadNotices = 0, bool sendLinks = false})
final List<InAppMessage> messages
final int nextCheckinSeconds

The soonest to check in again.

final bool sendLinks

PurrCRM has not been sent the destinations the check-in’s digest names: the app reports them (OutboundApi.reportLinks), once a run.

final bool testPhone

The guest is one of staff’s test phones: the app offers to check for tests at once.

final bool throttled

Too soon after the last: nothing was looked at.

final int unread

The messenger’s unread count: staff parts the guest has not read, in every conversation, a chat staff started included.

final int unreadNotices

The inbox’s unread count: notices PurrCRM pushed the guest that they have not read, for the bell. 0 from a PurrCRM older than the inbox.

class CheckinBody

What the app says when it checks in.

factory CheckinBody.fromJson(Map<String, Object?> json)
const CheckinBody({required String appVersion, required CrmPlatform platform, required String sessionId, String timeZone = '', String screen = '', bool holdsForScreens = false, String linksDigest = ''})
final String appVersion
final bool holdsForScreens

Whether the app holds a message meant for some screens (InAppMessage.screens) until one is on: true once the host has named a screen this run, so that one that never names any is never sent a message it would hold for ever.

final String linksDigest

The digest of the destinations the host declares (linksDigest), or empty when it declares none: a CRM that has not been sent them asks for them (Checkin.sendLinks).

final CrmPlatform platform
final String screen

The screen the guest is on, for a message aimed at one.

final String sessionId

The app’s session (the tracker’s), which a large message is shown once in.

final String timeZone

The phone’s IANA zone, for quiet hours and the guest’s day.

Map<String, Object?> toJson()
class DeclaredLink

One place in the host a link may go, as the host declares it (CrmHost.links): its name for staff, its path template (/offers/{offer_id}), and optionally its page on the web and its screen’s name.

factory DeclaredLink.fromJson(Map<String, Object?> json)
const DeclaredLink({required String name, required String path, String webUrl = '', String screen = ''})
final String name
final String path
final String screen
final String webUrl
Map<String, Object?> toJson()
class ExportReceipt

A message the guest was sent or shown, in their export.

factory ExportReceipt.fromJson(Map<String, Object?> json)
const ExportReceipt({required String message, required MessageKind kind, String variant = '', DateTime? sentAt, DateTime? seenAt, DateTime? clickedAt, DateTime? dismissedAt, DateTime? completedAt})
final DateTime? clickedAt
final DateTime? completedAt
final DateTime? dismissedAt
final MessageKind kind
final String message

The message’s name, as staff called it.

final DateTime? seenAt
final DateTime? sentAt
final String variant
class ExportSurveyAnswer

One answer, as the question’s words and the answer’s.

factory ExportSurveyAnswer.fromJson(Map<String, Object?> json)
const ExportSurveyAnswer({required String question, required String answer})
final String answer
final String question
class ExportSurveyResponse

The guest’s answers to one survey, in their export.

factory ExportSurveyResponse.fromJson(Map<String, Object?> json)
const ExportSurveyResponse({required String survey, required List<ExportSurveyAnswer> answers, required DateTime startedAt, DateTime? completedAt})
final List<ExportSurveyAnswer> answers
final DateTime? completedAt
final DateTime startedAt
final String survey
class ExportTestPhone

The guest’s account being a test phone, in their export: staff’s name for it, and since when.

factory ExportTestPhone.fromJson(Map<String, Object?> json)
const ExportTestPhone({String label = '', required DateTime addedAt})
final DateTime addedAt
final String label
class ExportTestSend

One test staff sent to the guest’s test phone, in their export: what it was, and what became of it.

factory ExportTestSend.fromJson(Map<String, Object?> json)
const ExportTestSend({required String name, bool sample = false, required MessageKind kind, required DateTime sentAt, DateTime? pickedUpAt, DateTime? seenAt, DateTime? clickedAt, DateTime? dismissedAt, DateTime? completedAt, String outcome = '', List<ExportSurveyAnswer> answers = const <ExportSurveyAnswer>[]})
final List<ExportSurveyAnswer> answers
final DateTime? clickedAt
final DateTime? completedAt
final DateTime? dismissedAt
final MessageKind kind
final String name

The message’s name, as staff called it, or the built-in sample’s.

final String outcome

A push’s or a chat’s: sent, notifications_off, not_sent or started.

final DateTime? pickedUpAt

When the app took an in-app test at a check-in.

final bool sample
final DateTime? seenAt
final DateTime sentAt
class InAppBanner

A strip across the screen.

factory InAppBanner.fromJson(Map<String, Object?> json)
const InAppBanner({required BannerStyle style, required List<ContentBlock> blocks, bool dismissible = true, String link = ''})
final List<ContentBlock> blocks
final bool dismissible
final String link

Where tapping it goes; empty for nowhere.

final BannerStyle style
class InAppMessage

One in-app message: exactly one of its contents, by kind.

factory InAppMessage.fromJson(Map<String, Object?> json)
const InAppMessage({required String id, required String variant, required MessageKind kind, InAppBanner? banner, InAppPost? post, Carousel? carousel, Survey? survey, bool test = false, List<String> screens = const <String>[]})
final InAppBanner? banner
final Carousel? carousel
final String id
final MessageKind kind
final InAppPost? post
final List<String> screens

The host’s screens it is shown on (Crm.screen’s names): held until one of them is on. Empty for any screen.

final Survey? survey
final bool test

A copy staff sent to a test phone: id is the test’s. Shown at once, whatever the caps, and what the guest does with it goes back marked as a test, so it never counts in the message’s report.

final String variant

Which of its A/B variants this is; every receipt carries it back.

bool get drawable

Whether the package can draw it: a kind it knows, with its content.

bool get large

Whether it takes the whole screen: a post, a carousel or a large survey. The app shows one of these a session at most.

class InAppPost

A full-screen message, from a teammate or from the product.

factory InAppPost.fromJson(Map<String, Object?> json)
const InAppPost({required List<ContentBlock> blocks, Author? sender})
final List<ContentBlock> blocks
final Author? sender
class OpenMessageBody

The message a link opened: the session it is shown in.

factory OpenMessageBody.fromJson(Map<String, Object?> json)
const OpenMessageBody({required String sessionId})
final String sessionId
Map<String, Object?> toJson()
class ReceiptBody

What the guest did with a message.

factory ReceiptBody.fromJson(Map<String, Object?> json)
const ReceiptBody({required String messageId, required String variant, required ReceiptAction action, String link = '', String sessionId = '', bool test = false, String screen = '', String button = '', List<String> answers = const [], List<String> path = const []})
final ReceiptAction action
final List<String> answers

The options picked on screen, for answered: one for a single choice, up to eight for a multiple.

final String button

A carousel’s button tapped, for clicked (with link too, for a link button).

final String link

The link followed, for clicked.

final String messageId
final List<String> path

The carousel’s screens the guest has seen so far, in order, a screen seen twice in it twice; at most maxReceiptPath, the first ones kept.

final String screen

A carousel’s screen: the one answered, the one whose button was tapped, the one it ended or was closed on.

final String sessionId
final bool test

About a test copy (InAppMessage.test): messageId is the test’s.

final String variant
Map<String, Object?> toJson()
class Survey

A survey: steps of questions, one after another, a branch skipping ahead on an answer, ending at a thank-you step or the last.

factory Survey.fromJson(Map<String, Object?> json)
const Survey({required SurveyFormat format, required List<SurveyStep> steps, List<ContentBlock> intro = const <ContentBlock>[]})
final SurveyFormat format
final List<ContentBlock> intro

A large survey’s first screen, before its questions; may be empty.

final List<SurveyStep> steps
SurveyQuestion? question(String id)

The question with id, or null.

class SurveyAnswer

One answer: a number, options, or words, by the question’s type.

factory SurveyAnswer.fromJson(Map<String, Object?> json)
const SurveyAnswer({required String questionId, int? number, List<String> choices = const <String>[], String other = '', String text = ''})
final List<String> choices
final int? number
final String other

The words with “Other”.

final String questionId
final String text
bool get answered

Whether it has anything in it.

Map<String, Object?> toJson()
class SurveyAnswerBody

A survey’s answers so far.

factory SurveyAnswerBody.fromJson(Map<String, Object?> json)
const SurveyAnswerBody({required String variant, required List<SurveyAnswer> answers, bool test = false})
final List<SurveyAnswer> answers
final bool test

A test copy’s (InAppMessage.test), by the test’s id.

final String variant
Map<String, Object?> toJson()
class SurveyBranch

A step’s way forward on an answer: a number in [min, max], or a choice among values, goes to next.

factory SurveyBranch.fromJson(Map<String, Object?> json)
const SurveyBranch({required String questionId, required String next, List<String> values = const <String>[], int? min, int? max})
final int? max
final int? min
final String next

The step it goes to, by id.

final String questionId
final List<String> values
class SurveyOption

One option of a choice question.

factory SurveyOption.fromJson(Map<String, Object?> json)
const SurveyOption({required String id, required String label})
final String id
final String label
class SurveyQuestion

One question.

factory SurveyQuestion.fromJson(Map<String, Object?> json)
const SurveyQuestion({required String id, required QuestionType type, required String title, String description = '', bool required = false, List<SurveyOption> options = const <SurveyOption>[], bool other = false, int min = 0, int max = 0, String minLabel = '', String maxLabel = '', String placeholder = ''})
final String description
final String id
final int max
final String maxLabel
final int min

A numeric question’s range.

final String minLabel

The words under a scale’s two ends.

final List<SurveyOption> options
final bool other

A choice question offering “Other”, with words of the guest’s own.

final String placeholder
final bool required
final String title
final QuestionType type
(int, int) get range

The numbers a scaled question is answered with.

class SurveyStep

One step of a survey.

factory SurveyStep.fromJson(Map<String, Object?> json)
const SurveyStep({required String id, required SurveyStepKind kind, List<SurveyQuestion> questions = const <SurveyQuestion>[], List<ContentBlock> blocks = const <ContentBlock>[], List<SurveyBranch> branches = const <SurveyBranch>[]})
final List<ContentBlock> blocks

What a thank-you step says.

final List<SurveyBranch> branches
final String id
final SurveyStepKind kind
final List<SurveyQuestion> questions
class SurveySubmit

A survey finished: every answer on the path the guest took.

factory SurveySubmit.fromJson(Map<String, Object?> json)
const SurveySubmit({required String variant, required List<SurveyAnswer> answers, bool test = false})
final List<SurveyAnswer> answers
final bool test

A test copy’s (InAppMessage.test), by the test’s id.

final String variant
Map<String, Object?> toJson()
const String carouselEnd

The target that ends a carousel. The Go constant is CarouselEnd.

const Duration checkinEvery

The least time between two check-ins; a sooner one is answered throttled. The Go constant is CheckinEvery.

const int maxCarouselScreens

The most screens a carousel has. The Go constant is MaxCarouselScreens.

const int maxLongAnswerRunes

The longest long answer. The Go constant is MaxLongAnswerRunes.

const int maxOtherRunes

The longest words for “Other”. The Go constant is MaxOtherRunes.

const int maxReceiptPath

The most screens a receipt’s path names. The Go constant is MaxReceiptPath.

const int maxShortAnswerRunes

The longest short answer. The Go constant is MaxShortAnswerRunes.

const String otherOption

The option id of “Other” on a choice question that allows it. The Go constant is OtherOption.