Walkthrough: bulk feedback import
This walkthrough tours the Bulk feedback import console page end to end: what a feedback outcome is, the shape of one record, how the wizard validates a CSV, why re-importing is safe, and where the imported outcomes surface. For the conceptual overview, see the Bulk feedback import guide; this page is the hands-on tour.1. When feedback matters
Orbit records a message’s delivery lifecycle — queued, sent, delivered, failed — from the carrier’s receipts. What delivery receipts can’t tell you is whether the recipient converted: opened the app, redeemed the offer, booked the appointment. That outcome usually lives in an external system — a mobile-measurement partner (MMP), your CRM, or an attribution vendor. Feedback closes that loop. Posting an outcome back onto each message stamps itconfirmed (the intended business outcome happened) or unconfirmed (it explicitly did not). Once stamped, Insights surfaces and attribution reports can correlate conversion against delivery status, channel, campaign, and template — so macro-level quality reads (per-campaign conversion, per-template lift) work off real outcomes instead of guesses.
Bulk import is how those outcomes arrive when the external system reports on a cadence — a nightly MMP conversion file, a weekly CRM export, a one-off vendor backfill.
2. The shape of one record
Each row is exactly two fields:confirmed covers positive conversion signals (delivered-and-read, link clicked, purchase, booking); unconfirmed covers messages you positively know did not convert. The enum is deliberately closed — the API rejects anything outside these two values, which keeps every downstream consumer of the feedback signal working off one vocabulary. Delivery lifecycle states (delivered, failed) come from carrier receipts, not from feedback.
Optionally, the same endpoint accepts a Twilio-shaped envelope (Sid / Outcome field names) so an integration written against Twilio’s Message Feedback API ports unchanged.
3. The bulk import wizard
Open Messages → Bulk feedback import (/messages/feedback). The page is gated to owner, admin, and developer roles; members without one of those roles see the nav entry but cannot reach the form.
Click Import feedback CSV, then paste or upload a two-column file:
- A header row is detected and skipped when its first cell is
id,sid,message_id, ormessagesid— so a Twilio-shapedSid,Outcomeexport and a Devotelid,outcomeexport both import without manual stripping. - Outcome values are case-insensitive (
Confirmed,CONFIRMEDare accepted and lower-cased). - Ids must match
msg_<32 hex>; legacy or wrong-shaped ids are listed as per-line errors with the line number, never guessed. - Duplicate ids are collapsed — first row wins — and the dialog reports how many duplicates it dropped.
- A batch accepts up to 1,000 valid rows. Rows beyond the cap are dropped with a truncation warning; split larger exports into batches.
4. Idempotency and retry semantics
Re-importing is safe. Each row lands through the same write path as the single-message endpoint (POST /api/v1/messages/:id/feedback):
- Re-posting the same outcome for a message is a no-op — the stamped timestamp is not rewritten.
- Flipping the outcome (e.g.
unconfirmed→confirmed) refreshes the stamped timestamp, so the audit trail reflects the correction. - Feedback writes touch only your own tenant’s message rows. There is no billing effect and no delivery side-effect.
5. Reading the result
The API response returns 200 even when some ids match nothing, and breaks the batch into two lists:updated[]— rows the write touched, with the stamped outcome and timestamp.not_found[]— ids that matched no message in your account (retention purge, wrong tenant, or stale export). In the dashboard these render as an explicit list under Ids not found in this account. Anot_foundrow is not worth retrying — reconcile the export upstream instead.
- Message detail — open a message in Messages and the stamped outcome appears alongside its delivery timeline.
- Insights goals and attribution — the feedback signal joins campaign, template, and channel lineage, so per-campaign and per-template conversion reads can reconcile against the imported outcomes. For paid campaigns, the Campaign ROAS and revenue attribution guide shows how the credited-revenue side reads; imported outcomes give it a conversion-side signal.
6. The API path
The same import is available as a single endpoint,POST /api/v1/messages/feedback/bulk, accepting 1 to 1,000 rows per request:
{"Items": [{"Sid": "…", "Outcome": "confirmed"}]}) is accepted as an alias, so a Twilio-integration bulk importer ports without a body change. A bulk import writes one audit-log entry for the batch with the requested / updated / not-found counts, so the import is traceable in your audit log as a single event rather than a thousand rows.