Reviews
The Reviews resource exposes the review_requests table, records of every review solicitation (SMS, email, or other channel) that BenchKey has sent, skipped, or queued for a customer. You can list and retrieve these records, and manually trigger a new review request for any ticket or invoice.
Review requests are channel-agnostic: BenchKey chooses the delivery channel (SMS, email, etc.) based on your shop’s settings and the customer’s consent state. The POST /reviews/request endpoint kicks off that same pipeline, it respects suppression lists, opt-out flags, and channel configuration.
The review object
Section titled “The review object”{ "object": "review", "id": 1042, "ticket_id": "23115", "invoice_id": null, "trigger_kind": "auto", "status": "sent", "carrier": null, "tracking_number": null, "channels": "sms", "armed_by": null, "customer_name": "Marcus Webb", "customer_phone": "(617) 555-0104", "customer_email": null, "device_type": "iPad Air 5", "ticket_total": "$285.76", "sent_via": "sms", "sent_at": "2026-09-28T16:55:06.000Z", "skipped_at": null, "clicked_at": null, "destination_url": "https://g.page/r/brightfix-repair/review", "send_after": "2026-09-28T16:55:06.000Z", "reminder_sent_at": null, "created_at": "2026-09-28T14:55:06.000Z"}| Field | Type | Description |
|---|---|---|
id | integer | Unique review request ID |
ticket_id | string|null | Ticket this request is tied to, or null for invoice-only requests |
invoice_id | integer|null | Invoice ID, or null for ticket-only requests |
trigger_kind | string|null | How this request was triggered: "auto", "manual", or null |
status | string | Current lifecycle state: pending, armed, scheduled, sending, sent, skipped, or cancelled |
carrier | string|null | Shipping carrier (populated when triggered by a shipment event), or null |
tracking_number | string|null | Shipment tracking number, or null |
channels | string|null | Delivery channels used (e.g. "sms", "email") |
armed_by | string|null | User or system that armed the request, or null |
customer_name | string|null | Customer name at time of send |
customer_phone | string|null | Customer phone at time of send |
customer_email | string|null | Customer email at time of send |
device_type | string|null | Device type on the ticket at time of send |
ticket_total | string|null | Ticket total at time of send (formatted string) |
sent_via | string|null | Channel actually used to deliver the request |
sent_at | string|null | ISO-8601 timestamp when the request was sent, or null |
skipped_at | string|null | ISO-8601 timestamp when the request was skipped, or null |
clicked_at | string|null | ISO-8601 timestamp when the customer clicked the review link, the conversion signal for reputation integrations |
destination_url | string|null | The review destination the link points at (e.g. your Google review URL) |
send_after | string|null | ISO-8601 timestamp a scheduled request is held until, or null |
reminder_sent_at | string|null | ISO-8601 timestamp the follow-up reminder went out, or null |
created_at | string | ISO-8601 timestamp when the record was created |
The status lifecycle is armed → scheduled → sending → sent (or skipped / cancelled at any point before delivery); pending is the legacy pre-lifecycle state.
List review requests
Section titled “List review requests”GET /api/v1/reviewsReturns a cursor-paginated list of review requests ordered newest first. Requests tied to soft-deleted or hidden tickets are excluded.
Scope required: reviews.read
Query parameters
Section titled “Query parameters”| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: pending, armed, scheduled, sending, sent, skipped, or cancelled |
sent_via | string | Filter by delivery channel (e.g. "sms", "email") |
ticket_id | string | Filter to requests tied to a specific ticket |
limit | integer | Page size, 1–100 (default: 20) |
cursor | string | Opaque cursor from a previous response’s next_cursor |
curl "https://app.benchkey.com/api/v1/reviews?status=sent&limit=10" \ -H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa"Node.js
Section titled “Node.js”const res = await fetch( "https://app.benchkey.com/api/v1/reviews?status=sent&limit=10", { headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" } });const { data, has_more, next_cursor } = await res.json();Response
Section titled “Response”{ "object": "list", "data": [ { "object": "review", "id": 1042, "ticket_id": "23115", "invoice_id": null, "trigger_kind": "auto", "status": "sent", "carrier": null, "tracking_number": null, "channels": "sms", "armed_by": null, "customer_name": "Marcus Webb", "customer_phone": "(617) 555-0104", "customer_email": null, "device_type": "iPad Air 5", "ticket_total": "$285.76", "sent_via": "sms", "sent_at": "2026-09-28T16:55:06.000Z", "skipped_at": null, "clicked_at": null, "destination_url": "https://g.page/r/brightfix-repair/review", "send_after": "2026-09-28T16:55:06.000Z", "reminder_sent_at": null, "created_at": "2026-09-28T14:55:06.000Z" } ], "has_more": false, "next_cursor": null}See Pagination for how to page through results.
Retrieve a review request
Section titled “Retrieve a review request”GET /api/v1/reviews/:idReturns a single review request record.
Scope required: reviews.read
curl "https://app.benchkey.com/api/v1/reviews/1042" \ -H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa"Node.js
Section titled “Node.js”const res = await fetch( "https://app.benchkey.com/api/v1/reviews/1042", { headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" } });const review = await res.json();Response
Section titled “Response”Returns the review object. Returns 404 if no review request with that ID exists, or if the associated ticket is soft-deleted or hidden.
Send a review request
Section titled “Send a review request”POST /api/v1/reviews/requestManually triggers a review request for a ticket or invoice. BenchKey runs the same pipeline as its automatic review triggers, it checks suppression lists, opt-out flags, consent state, and channel settings before sending. The endpoint returns 202 Accepted regardless of whether a message was actually delivered; check the sent and skipped fields in the response body.
Scope required: reviews.write
You must provide at least one of ticket_id or invoice_id.
- If you supply only
invoice_id, the ticket is derived automatically from the invoice’s linked ticket. If the invoice has no linked ticket, the request returns400 invalid_field. - If you supply both, they must be consistent, the invoice must belong to the given ticket. A mismatch returns
400 invalid_fieldwith code"invoice_id does not belong to ticket_id.".
Request body
Section titled “Request body”| Field | Required | Description |
|---|---|---|
ticket_id | conditional | Ticket to send the review request for |
invoice_id | conditional | Invoice ID to send the review request for. If ticket_id is omitted, the ticket is derived from the invoice |
curl -X POST https://app.benchkey.com/api/v1/reviews/request \ -H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" \ -H "Content-Type: application/json" \ -d '{ "ticket_id": "TK-2091" }'Node.js
Section titled “Node.js”const res = await fetch("https://app.benchkey.com/api/v1/reviews/request", { method: "POST", headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa", "Content-Type": "application/json", }, body: JSON.stringify({ ticket_id: "TK-2091" }),});const result = await res.json(); // always 202Response
Section titled “Response”{ "object": "review_request_result", "ok": true, "sent": true, "skipped": false, "reason": null, "ticket_id": "TK-2091", "invoice_id": null}| Field | Type | Description |
|---|---|---|
ok | boolean | Always true when the pipeline ran without error |
sent | boolean|null | true if a message was dispatched to the customer |
skipped | boolean|null | true if the pipeline decided not to send (opt-out, suppression, etc.) |
reason | string|null | Human-readable reason the request was skipped, or null |
ticket_id | string|null | Echo of the provided ticket ID |
invoice_id | integer|null | Echo of the provided invoice ID |
Error codes
Section titled “Error codes”| HTTP status | Code | Meaning |
|---|---|---|
400 | invalid_id | The :id in the URL is not a valid positive integer |
400 | invalid_query | A list filter parameter was supplied as an array or object instead of a single scalar value |
400 | invalid_field | A field value is invalid, e.g. status not in the lifecycle set (pending/armed/scheduled/sending/sent/skipped/cancelled), invoice_id not a positive integer or exceeds 2,147,483,647, the invoice has no linked ticket (when ticket_id is omitted), or invoice_id does not belong to the supplied ticket_id |
400 | invalid_sent_via | sent_via filter is present but empty |
400 | invalid_ticket_id | ticket_id filter is present but empty |
400 | invalid_cursor | cursor is malformed |
400 | missing_field | Neither ticket_id nor invoice_id was provided |
404 | not_found | No review request with that ID exists, or its ticket is deleted/hidden |
403 | insufficient_scope | API key lacks reviews.read or reviews.write |
422 | request_failed | The review request pipeline returned an error |
See Errors for the full error envelope format.