BitelioBitelio

Surveys in WhatsApp and your app

Preserve the customer's questions, answers and progress across channels

A notification and a survey response are different events. Opening a message does not answer a question. Bitelio keeps one invitation and one saved draft across the browser, WhatsApp and a native mobile app, and validates every answer against its currently available published questions.

For dashboard setup first, read Surveys that adapt and Channels and customer help. This guide covers technical integration; enabling a channel does not replace its connection or the customer’s consent.

WhatsApp

Connect the merchant's own WhatsApp Business number under Second purchase → Settings → Channels. The customer must have current WhatsApp consent. Select an approved marketing template for each survey language with fixed text and this URL button:

https://app.bitelio.com/surveys/respond#token={{1}}

To let customers answer inside WhatsApp, add an approved quick-reply button labelled Answer here, Answer survey or Start survey. The Spanish labels Responder aquí, Responder encuesta and Comenzar encuesta are also supported. Keep the URL button so customers can always continue in the browser. Refresh templates after Meta approves the change. The settings screen shows whether the selected template supports native answers or only a link.

When the customer presses that button, Bitelio sends the next visible question. Single-choice questions use reply buttons or a list; ratings are supported when the available options fit. Each answer saves progress. A final Send answers confirmation completes the survey and applies its insights. The subsequent help and recommendations use the existing survey link.

CapabilityWhatsApp behaviour
Conditional single-choice questionsNative; the next question follows previous answers
Up to 10 choices, including an optional SkipButtons or list, preserving complete labels up to 72 characters
Multiple-choice, longer labels, more choices, unsupported languageContinue using the same invitation link and saved progress
Native interface languagesEnglish and Spanish; other languages keep their browser survey
Free text, audio, imagesNever interpreted as a survey answer; existing help handling applies
Customer service windowNative replies only within 24 hours of a verified customer reply

Outside that window Bitelio does not send unapproved session messages. The original approved URL remains available. Pressing the original start button again can begin a new native conversation while the invitation is valid. Changing a previous answer in the browser invalidates stale WhatsApp choices; reopen the invitation to continue from the current draft.

Provider webhooks must preserve the exact raw request body for Meta signature verification. Bitelio also binds every choice to the merchant, customer phone, invitation, exact outbound message, question, option, draft revision and expiry. Duplicate webhooks cannot duplicate a response or an accepted next message. A network timeout with uncertain provider acceptance is not automatically resent.

Native turns are separate provider sends. They are not assumed free; unpriced attempts remain visible as unknown provider costs until matched with cost evidence.

Import an evidenced provider invoice line through POST /v1/retention/messages/cost-receipts, using turnId or the exact providerMessageId for a native turn. deliveryId identifies the initial invitation only.

Provider contracts: Meta reply buttons and Meta template quick replies.

Native mobile integration

Your backend delivers notifications through your existing APNs/FCM integration. Bitelio does not receive device notification credentials. Enable Mobile API, register the customer's permission through POST /v1/retention/mobile-permissions, claim an invitation, and acknowledge actual provider acceptance or delivery. An unaccepted handoff cannot create a native session.

1. Create a short-lived session on your backend

Authenticate the customer in your own app first. Resolve their Bitelio contact and an opaque device identifier on your server; never trust an arbitrary contact ID posted by the app. Keep the claim's receipt token and your Bitelio secret key on the server.

POST /v1/retention/mobile-survey-sessions
Authorization: Bearer sk_YOUR_SECRET_KEY
Content-Type: application/json

{
  "deliveryId": "DELIVERY_UUID",
  "contactId": "CONTACT_ID",
  "receiptToken": "SERVER_SIDE_RECEIPT_UUID",
  "deviceId": "opaque-device-identifier"
}

The key needs retention:view and retention:edit. deviceId must contain 16–128 letters, digits, _ or -; use an opaque value, not an email or phone. The response contains sessionToken, expiresAt and supported capabilities. The session lasts at most 15 minutes and is bound to that device, delivery and customer. Permission changes invalidate it. This is a device binding based on your backend's authentication, not hardware attestation.

Give the app only the session token, expiry and device identifier over your authenticated connection. Store them in protected app storage, never in notification text, analytics or URLs. Request a new session from your backend when it expires.

2. Render the actual survey

GET /public/retention/mobile-survey
X-Bitelio-Mobile-Session: SESSION_TOKEN
X-Bitelio-Device: opaque-device-identifier

The response uses BuyerSurveyView: state, branding, locale, questions and saved draft. Render only OPEN surveys. Walk questions in their declared order and apply each showWhen rule to visible earlier answers. Never show or submit a hidden branch. Support single-choice, multiple-choice and rating controls; do not replace them with free text. Restore the draft's answers and throughQuestionId on resume. A completed response may include completion, help and nextStep; preserve their real choices and available actions.

3. Save a completed step

On an explicit Continue action, post the complete visible prefix, including previous answers:

POST /public/retention/mobile-survey/progress
X-Bitelio-Mobile-Session: SESSION_TOKEN
X-Bitelio-Device: opaque-device-identifier
Content-Type: application/json

{
  "revision": 0,
  "throughQuestionId": "recipient",
  "answers": [{"questionId": "recipient", "value": "self"}]
}

Use the question IDs and option values returned by the API, not the example values above. Multiple-choice values are arrays of option values; ratings are numbers. Saving a step does not complete the survey or publish customer insights. Keep the returned revision. If a previous answer changes, remove answers from any hidden or not-yet-confirmed later steps before saving again.

4. Submit only after confirmation

POST /public/retention/mobile-survey/submit
X-Bitelio-Mobile-Session: SESSION_TOKEN
X-Bitelio-Device: opaque-device-identifier
Content-Type: application/json

{"revision": 1, "answers": [{"questionId": "recipient", "value": "self"}]}

Include every required visible answer. Exact retries are idempotent. A 409 SURVEY_DRAFT_CONFLICT means another device changed the draft: reload and let the customer reconcile it, rather than overwriting silently. A 404 means the session or permission is unavailable; return to your authenticated backend. All native responses are private and non-cacheable. Public native calls are limited to 60 requests per session per minute; a 429 supplies Retry-After.

Buy again or ask for help without answering first

Using the same two native session headers, GET /public/retention/mobile-survey/actions returns the available actions. It records nothing. Offer these alongside the survey so a customer ready to buy does not have to complete it first.

POST /public/retention/mobile-survey/actions accepts an exact body such as {"action":"REPEAT","stage":"REQUESTED"} or {"action":"DISCOVER","stage":"REQUESTED"} and returns up to three current product choices. Render their real images, options and explanations. Record VIEWED only after a deliberate customer interaction; SELECTED and CHECKOUT require the exact productId and variantId from a returned choice. At checkout, use the returned url after Bitelio rechecks stock and eligibility. Never construct your own cart from an old suggestion.

{"action":"HELP","stage":"REQUESTED"} reads reviewed guidance. A separate explicit HELP_REQUESTED requests a person, only when canContact is true; RESOLVED records the customer's resolution and must include caseId and updatedAt copied from the helpRevision returned for the case shown. Never create these values locally. If Bitelio returns HELP_REPLY_CONFLICT, reload the case and ask the customer to review it before confirming again. Session/device permission is revalidated within each write. These actions do not submit or fabricate survey answers.

Browser push uses the same saved invitation in the browser. Do not add answers to email links or treat notification opens, GET requests or security scanners as survey responses.