Webhook operations
Open a delivery portal and create or update webhook destinations.
Webhook operations require the webhooks permission.
Events
Topics are named <resource>.<past_tense_verb>. Every delivery is a JSON POST with the same envelope, which follows the Standard Webhooks payload shape:
{
"id": "01926f3a-8a1e-7c52-9d0b-3c1f6e2a4b10",
"type": "opinion.created",
"timestamp": "2026-09-26T14:03:11.482Z",
"data": { "reviewId": "…", "projectId": "…" }
}id is unique per event and stays the same when a delivery is retried, so use it to make processing idempotent. timestamp is when the change happened. data holds the resource as it was after the change.
Opinions
| Topic | Sent when |
|---|---|
opinion.created | A customer submits an opinion. data includes the title, the opinion text, the store, and the customer (anonymous customers have no id). Each answer they gave arrives as its own answer.created. |
opinion.updated | A customer edits a submitted opinion. data is the full opinion plus changes, which lists title and/or opinion. |
opinion.deleted | A customer deletes a submitted opinion, along with its answers. data contains reviewId and projectId. |
Answers
Every answer to a project question is sent separately, whether it was given with an opinion or through a widget. data includes answerId, reviewId, questionId, the question text and its tags, the answer, any selectedOptions, and the customer.
| Topic | Sent when |
|---|---|
answer.created | A customer answers a project question. |
answer.updated | A customer changes their answer. |
Comments
| Topic | Sent when |
|---|---|
comment.created | A customer comments on a review, or your team responds to one. data.author.type is customer or business. |
comment.updated | A comment or team response is edited. |
comment.deleted | A customer deletes their comment. data contains commentId, reviewId, and projectId. |
Verifications
Verification events share one payload: verificationId, projectId, reviewId, method (visual_evidence, delivery_link, qr_permanent, or qr_rotating), status, previousStatus, and occurredAt. Receipt images and extracted evidence are never included. Internal processing and manual-review steps are not emitted as separate webhook topics; verification.failed uses status to distinguish rejected, expired, and cancelled.
| Topic | Sent when |
|---|---|
verification.created | A verification starts. status is the status it starts in. |
verification.verified | The purchase is verified. |
verification.failed | The verification did not complete successfully. data.status is rejected, expired, or cancelled. |
Delivery links
Delivery link events share one payload, a snapshot of the link after the change: verificationId, projectId, customerEmail, customerReference, status (issued, clicked, submitted, or expired), reviewId, issuedAt, expiresAt, emailedAt, clickedAt, and submittedAt. The link URL is never included, because it grants access to the opinion form. reviewId is set only once a customer submits a named opinion with the link; incognito opinions are never connected to it.
| Topic | Sent when |
|---|---|
link.created | A delivery link is created for a customer. |
link.emailed | A delivery link is emailed to its customer. Sent each time it is emailed. |
link.clicked | The customer opens the link and starts an opinion with it. Sent once per link. |
link.submitted | The customer submits an opinion with the link, which verifies it. The matching verification.verified is sent too. |
Subscribers
| Topic | Sent when |
|---|---|
subscriber.created | A customer subscribes to a project, directly or by accepting an invitation. |
subscriber.deleted | A customer unsubscribes, or your team removes them. data.blocked is true when your team removed them. |
Projects, questions, and QR codes
| Topic | Sent when |
|---|---|
project.created, project.updated, project.deleted | A project is created, changed (name, slug, description, status, or settings), or deleted. |
question.created, question.updated, question.deleted | A project question is added, edited, activated or archived, or removed. |
qr_code.created, qr_code.regenerated, qr_code.revoked | An opinion QR code is created, regenerated (invalidating printed copies), or revoked. |
webhooks.portal
Open portal request
POST /webhooks/portal
import { webhooks } from '@orvelt/business';
const result = await webhooks.portal({
theme: 'dark',
});
if (result.error) throw new Error(result.error.message);
console.log(result.data);curl --request POST \
--url "$ORVELT_API_BASE_URL/webhooks/portal" \
--header "x-api-key: $ORVELT_BUSINESS_API_KEY" \
--header "content-type: application/json" \
--data '{"theme":"dark"}'theme accepts light or dark.
webhooks.destinations.upsert
Save destination request
POST /webhooks/destinations
import { webhooks } from '@orvelt/business';
const result = await webhooks.destinations.upsert({
url: 'https://example.com/webhooks/orvelt',
topics: ['opinion.created', 'answer.created', 'verification.verified'],
});
if (result.error) throw new Error(result.error.message);
console.log(result.data);curl --request POST \
--url "$ORVELT_API_BASE_URL/webhooks/destinations" \
--header "x-api-key: $ORVELT_BUSINESS_API_KEY" \
--header "content-type: application/json" \
--data '{"url":"https://example.com/webhooks/orvelt","topics":["opinion.created","answer.created","verification.verified"]}'Include destinationId to update an existing destination. url is required; topics can contain any non-empty subset of the supported topics. Leave topics out to receive every topic.