Skip to content

Approve a held payment

The risk.reviews.approve operation: POST /api/v1/risk/reviews/{id}/approve in the REST API and the risk_reviews_approve tool in the MCP server.

POST/api/v1/risk/reviews/{id}/approve

Releases a payment the risk rules held: the customer's wallets the hold froze are unfrozen (unless another review of the customer is still pending, or someone changed the wallet since), the hold's reason leaves the customer's risk (back to normal when nothing else elevated it), and risk.review.approved goes to your webhook endpoints, so your integration can deliver what it held back. A review is decided once.

At a glance

  • REST: POST /api/v1/risk/reviews/{id}/approve
  • MCP tool: risk_reviews_approve
  • Operation ID: risk.reviews.approve
  • Scope: Brand
  • Permission: customers:write
  • Risk tier: write
  • Dry run: Supported (send Ownrecord-Dry-Run: true)
  • Idempotency: Idempotency-Key required
  • Modes: Test and live
  • Authentication: API key with the customers:write scope
  • Since: M9

Request

Path parameters

FieldTypeDescription
idrsk_… ID

Body

A JSON object (Content-Type: application/json):

FieldTypeRequiredDescription
notestringNo
expectedVersioninteger (at least 1)No

Response

200 OK with a JSON object:

FieldTypeDescription
idrsk_… ID
object"risk_evaluation"
livemodeboolean
brandIdbrd_… ID
subjectobject
subject.typeone of checkout_session, auto_reload
subject.checkoutSessionIdcs_… ID or null
subject.walletIdwal_… ID or null
customerIdcus_… ID or null
paymentIdpay_… ID or null
stageone of intent, payment_method, confirm, off_session
decisionone of allow, require_3ds, review, block
scoreinteger
reasonsarray of object
reasons[].codeone of 21 values, e.g. customer_blocked, customer_elevated, blocklist_email (all in the OpenAPI document)
reasons[].actionone of block, review, require_3ds, score
reasons[].pointsinteger or null
reasons[].detailstring
signalsobject
signals.emailDomainstring or null
signals.ipNetworkstring or null
signals.ipCountrystring or null
signals.billingCountrystring or null
signals.cardCountrystring or null
signals.cardFingerprintstring or null
signals.amountmoney or null
signals.topupboolean
signals.newCustomerboolean
signals.firstPaymentboolean
signals.disposableEmailboolean
historyarray of object
history[].atdate-time
history[].stageone of intent, payment_method, confirm, off_session
history[].decisionone of allow, require_3ds, review, block
history[].scoreinteger
reviewobject or null
createdAtdate-time
updatedAtdate-time

Examples

Approve after calling the customer

HTTP
POST /api/v1/risk/reviews/rsk_01m4ttwd3veh9apbewxheq5xr1/approve
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "note": "Called the customer; the card is a company card."
}

Response (200):

JSON
{
  "id": "rsk_01m4ttwd3veh9apbewxheq5xr1",
  "object": "risk_evaluation",
  "livemode": true,
  "brandId": "brd_01m4ttwd3veh9apbewxheq5xh5",
  "subject": {
    "type": "checkout_session",
    "checkoutSessionId": "cs_01m4ttwd3veh9apbewxheq5xr4",
    "walletId": null
  },
  "customerId": "cus_01m4ttwd3veh9apbewxheq5xr5",
  "paymentId": "pay_01m4ttwd3veh9apbewxheq5xr6",
  "stage": "confirm",
  "decision": "review",
  "score": 70,
  "reasons": [
    {
      "code": "first_payment",
      "action": "require_3ds",
      "points": null,
      "detail": "The customer's first payment."
    },
    {
      "code": "new_customer",
      "action": "score",
      "points": 10,
      "detail": "A new customer."
    },
    {
      "code": "country_mismatch_ip_billing",
      "action": "score",
      "points": 20,
      "detail": "IP country DE isn't the billing country HR."
    },
    {
      "code": "country_mismatch_card_billing",
      "action": "score",
      "points": 25,
      "detail": "Card country US isn't the billing country HR."
    },
    {
      "code": "country_mismatch_ip_card",
      "action": "score",
      "points": 15,
      "detail": "IP country DE isn't the card country US."
    },
    {
      "code": "score_review",
      "action": "review",
      "points": null,
      "detail": "Score 70 ≥ 70."
    }
  ],
  "signals": {
    "emailDomain": "corp.example",
    "ipNetwork": "203.0.113.0",
    "ipCountry": "DE",
    "billingCountry": "HR",
    "cardCountry": "US",
    "cardFingerprint": "Xo1abC9dEf2GhI3j",
    "amount": {
      "amount": "90000000",
      "currency": "EUR"
    },
    "topup": true,
    "newCustomer": true,
    "firstPayment": true,
    "disposableEmail": false
  },
  "history": [
    {
      "at": "2026-10-20T09:00:00.000Z",
      "stage": "intent",
      "decision": "require_3ds",
      "score": 30
    },
    {
      "at": "2026-10-20T09:01:00.000Z",
      "stage": "payment_method",
      "decision": "review",
      "score": 70
    },
    {
      "at": "2026-10-20T09:01:30.000Z",
      "stage": "confirm",
      "decision": "review",
      "score": 70
    }
  ],
  "review": {
    "status": "approved",
    "openedAt": "2026-10-20T09:02:00.000Z",
    "decidedAt": "2026-10-20T11:30:00.000Z",
    "decidedBy": "usr_01m4ttwd3veh9apbewxheq5xh6",
    "note": "Called the customer; the card is a company card.",
    "refundId": null,
    "blocklistEntryIds": [],
    "frozenWalletIds": ["wal_01m4ttwd3veh9apbewxheq5xr7"]
  },
  "createdAt": "2026-10-20T09:00:00.000Z",
  "updatedAt": "2026-10-20T11:30:00.000Z"
}

Errors

CodeHTTP statusWhenWhat to do
resource_not_found404No risk evaluation with this ID in this brand and mode.Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.
risk_evaluation_not_held400The evaluation held no payment for review.Only payments the risk rules held are in the review queue (risk.reviews.list). Refund other payments with refunds.create.
risk_review_not_pending409The review was already approved or rejected.Reload the review: it was approved or rejected (details.status). A decision is final; refund the payment or unfreeze the wallet by hand if something must change.
version_conflict409The review changed since expectedVersion, or at the same moment.Fetch the object again and retry the change.
permission_denied403You can't change this brand's customers.Ask an organization owner or admin for a role or scope that allows this action.

It can also return these common errors: validation_failed, api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, scope_required, idempotency_key_required, idempotency_key_reused, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.