Skip to navigation

Verify on your server

Confirm the customer's approval from your server before you bind the policy.

How it works

1

Keep the review ID with the quote

When a review starts, you get its ID (rv_…). With the embed, onResult passes it to your page: send it to your server and store it against the quote.

checkout-approvals.js
HumanApproved.q.push(["protect", {
// …key, quote, before, quoteDetails
onResult: (r) => {
if (r.outcome === "approved") {
fetch("/api/quotes/HTA-4471920/approval", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ reviewId: r.reviewId }),
});
}
},
}]);

You can also send the ID with the payment form, for example in a hidden field.

In a server or agent flow, your server already has the ID from the check’s response.

2

Read the review at bind

Call the server endpoint with your secret key. It’s for servers only: it sends no CORS headers and needs no Origin. Set your secret key and your region’s address as environment variables:

export HUMANAPPROVED_URL="https://au.humanapproved.com" # your region's address
export HUMANAPPROVED_SECRET_KEY="sk_test_…" # servers only
curl $HUMANAPPROVED_URL/api/v1/server/reviews/$REVIEW_ID \
-H "Authorization: Bearer $HUMANAPPROVED_SECRET_KEY"
200 OK
{
"id": "rv_01jcz3fa1q8w3n5r7t9v1x3z5b",
"status": "approved",
"quoteRef": "HTA-4471920",
"userId": "CUST-1001",
"product": "acme-travel",
"productVersion": 11,
"env": "live",
"expiresAt": "2026-11-19T09:14:23.000Z",
"approvedAt": "2026-11-18T09:20:41.000Z",
"declinedAt": null,
"declineReason": null
}

A review from another account or environment returns 404 review_not_found: a test secret key only reads test reviews.

3

Check it before you bind

Bind only when every check passes:

FieldCheck
statusIs approved. Anything else (awaiting_quote, pending, declined, expired) means don’t bind.
quoteRefMatches the quote you’re about to bind. This stops an approval for one quote being reused for another.
envIs live in production. A test review is never a real approval.
productVersionIs the published version you expect. It’s the version whose wording the customer saw.

A purchase with no review ID is one the check let continue, or one that proceeded without an answer. This is expected for most customers when the product asks only when an agent is detected. Where every purchase must be approved, such as an agent flow or a product set to Every purchase, refuse to bind without an approved review.

Node
async function assertApproved(reviewId, quoteRef, productVersion) {
const res = await fetch(`${process.env.HUMANAPPROVED_URL}/api/v1/server/reviews/${reviewId}`, {
headers: { authorization: `Bearer ${process.env.HUMANAPPROVED_SECRET_KEY}` },
});
if (!res.ok) throw new Error(`Review check failed: ${res.status}`);
const review = await res.json();
if (review.status !== "approved") throw new Error(`Review is ${review.status}`);
if (review.quoteRef !== quoteRef) throw new Error("Review is for another quote");
if (review.env !== "live") throw new Error("Review is not live");
if (review.productVersion !== productVersion) throw new Error("Review is for another version of the product");
return review;
}
// At bind:
const review = await assertApproved(process.env.REVIEW_ID, "HTA-4471920", 11); // 11: the product version you published
// …bind the policy you hold on your server for HTA-4471920

This check confirms the customer approved the quote with that reference. It doesn’t compare the quote’s contents. With the embed, the quote the customer sees comes from quoteDetails in the browser, so build it from the quote your server holds, and issue a new quote reference whenever the details change. For the strongest assurance, create the review from your server so the quote never passes through the browser: see Server and agent flows.