Skip to main content
Version: v2

Unlinked Refunds API

Unlinked Refund allows merchants to issue a refund directly to a payment method (typically a card) without referencing a prior payment transaction. Unlike linked refunds that reverse a specific charge, unlinked refunds credit a payment method independently β€” useful for goodwill credits, resolving failed linked refunds, or special circumstances. This guide covers how to initiate unlinked refunds, monitor status, and handle errors and webhook notifications.

Standard refunds? For refunds tied to an existing payment, use linked refunds β€” see Split Tender Refunds. Linked refunds reference a paymentId and can be partial or full based on the original payment amount.

  • Refund model: Direct credit to payment method using paymentMethodId
  • Payment method types: CARD only β€” ACH and bank accounts not supported
  • Single refund per transaction: Only one unlinked refund allowed per (merchantId, merchantTransactionId) pair
  • Customer required: Explicit customer object with identifiers must be provided

When to Use​

Use Unlinked Refund when:

  • No prior payment exists β€” issuing a goodwill credit or promotional refund
  • Linked refund failed β€” original payment is unavailable, expired, or disputed
  • Special circumstances β€” manual resolution requires a direct credit to customer's card
  • Card replacement β€” customer has a new card and the old payment cannot be reversed

Note: Unlinked refunds are CARD-only and single-use per merchant transaction ID. The customer and payment method must be properly associated before processing.

Business Context

For business context, eligibility, scenarios see Unlinked Refunds β€” Business Document.


Integration Details
  • API Endpoint:
    • POST v2/refunds β€” create an unlinked refund
    • GET v2/refunds/{refundId} β€” poll refund status
  • Scope: All operations are at the merchant scope.
  • API Specs: Create Refund | Get Refund
  • Webhook Specs: Refund Webhooks
  • API: v2
  • Testing: Use Test Cards to simulate unlinked refund scenarios. See Test Cards
  • ⚠️ Error Handling: See Refund Error Codes for unlinked-specific errors and resolution steps

Refund Processing Flow​

Unlinked refunds are processed independently without referencing any prior payment transaction.

Refund Flow

Refund Processing Steps:

  1. Request received β€” Payment Service receives unlinked refund request with paymentMethodId and customer.
  2. Schema validation β€” Payment Service validates request structure and required fields β†’ 400 INVALID_REQUEST on failure.
  3. Business validation β€” Payment Service enforces business rules β†’ 422 UNPROCESSABLE_ENTITY on failure.
  4. Payment method validation β€” Payment Service calls Customer Service to get payment method details and checks if CARD type (rejects BANK/ACH).
  5. Customer resolution β€” Payment Service searches for customer in Customer Service; if not found, creates customer via /customer/find and sends event to Stripe.
  6. Association validation β€” Payment Service verifies customer and payment method are associated via Customer Service.
  7. Acceptance β€” If all validations pass, Payment Service returns 202 Accepted.
  8. Refund execution β€” Payment Service sends refund request to Stripe Adapter Service, which executes unlinked refund in Stripe (without charge reference).
  9. Stripe events β€” Stripe publishes refund events (refund.created, refund.updated, or refund.failed) to Stripe Adapter Service.
  10. Status update β€” Stripe Adapter Service sends VENDOR_MERCHANT_REFUND_SUCCESS or VENDOR_MERCHANT_REFUND_FAILED to Payment Service, which updates refund status.
  11. Merchant notification β€” Payment Service publishes event to Merchant Service, which sends REFUND_SUCCESS or REFUND_FAILED webhook to Merchant.
  12. Status polling β€” Merchant can poll GET /v2/refunds/{refundId} to retrieve refund details.

API Request​

API Specification
Sample Request β€” POST v2/refunds
curl -X POST "https://api-stg.uhg.com/api/financial/commerce/checkout/v2/refunds" \
-H "Authorization: <token>" \
-H "X-Merchant-Id: <merchantId>" \
-H "Content-Type: application/json" \
-d '{
"reason": "REQUESTED_BY_CUSTOMER",
"merchantTransactionId": "6f76fa67-b887-4c36-a486-b425b86befbd",
"customer": {
"firstName": "string",
"lastName": "string",
"hsid": "50daa5d5-37a1-48e8-91be-efd1743dbc2e",
"metadata": {}
},
"refundAllocations": [
{
"amount": 100,
"paymentMethodId": "09584dad-194e-455a-b980-0bb3abf10fe4"
}
]
}'

Request Field Details​

FieldRequiredTypeDescription
amountYesNumberRefund amount in cents (e.g., 5000 = $50.00). Must not exceed $10,000 USD (1000000).
reasonYesStringReason code (e.g., DUPLICATE, FRAUDULENT, REQUESTED_BY_CUSTOMER)
paymentMethodIdYesUUIDPayment method to be credited (must be CARD type)
merchantTransactionIdYesUUIDUnique merchant reference for idempotency and tracking
customerYesObjectCustomer information for validation
customer.firstNameYesStringCustomer first name
customer.lastNameYesStringCustomer last name
customer.hsidYesStringCustomer HSID identifier (primary identifier)
customer.metadataNoObjectAdditional customer metadata
Important
  • paymentId must be absent or null for unlinked refunds
  • If both paymentId and paymentMethodId are provided, the request is treated as a linked refund
  • Payment method must be CARD type β€” ACH and bank accounts will be rejected with 422 UNPROCESSABLE_ENTITY
  • Customer must exist and payment method must be associated with that customer

Poll Refund Status​

Refund processing is asynchronous β€” the API returns INITIATED immediately. Poll GET v2/refunds/{refundId} until the status settles to a terminal state (COMPLETED or FAILED).

Sample Request β€” GET v2/refunds/refundId
curl -X GET "https://api-stg.uhg.com/api/financial/commerce/checkout/v2/refunds/{refundId}" \
-H "Authorization: <token>" \
-H "X-Merchant-Id: <merchantId>" \
-H "Accept: application/json"

API Response​

Response Structure

The complex object is trimmed for brevity. Refer to the API Spec for the full response structure.

understanding refund API Response
  • Unlinked refunds do not include payment object since there's no associated payment
  • The paymentMethod object contains the credited card details
  • Top-level status shows refund outcome: COMPLETED, FAILED, or PENDING
  • For FAILED, check the error object for details
  • No refundAllocations array for unlinked refunds β€” single direct credit
  • For all error codes and troubleshooting, see Refund Error Codes.

Final Response β€” COMPLETED​

Sample Response β€” COMPLETED (HTTP 200)
{
"url": "http://api-stg.uhg.com:443/api/financial/commerce/nonprodcheckout/v2/refunds/1b9683da-5f0b-4444-8c5a-3958bd67353f",
"data": {
"id": "1b9683da-5f0b-4444-8c5a-3958bd67353f",
"status": "COMPLETED",
"reason": "REQUESTED_BY_CUSTOMER",
"merchantTransactionId": "6f76fa67-b887-4c36-a486-b425b86befbd",
"metadata": {
"orderId": "123486",
"purpose": "issueCredit"
},
"merchant": {
"id": "b955db5e-aef2-47de-bbb9-c80b9cc16e8f"
},
"refundAllocations": [
{
"id": "f771d07c-c827-41a4-8dbd-6791ff390f00",
"amount": 100,
"status": "COMPLETED",
"paymentMethod": {
"id": "09584dad-194e-455a-b980-0bb3abf10fe4",
"nickname": "string",
"paymentMethodDetails": {
"type": "CARD",
"last4": "4242",
"expiryMonth": 12,
"expiryYear": 2045,
"nameOnCard": "string",
"cardBrand": "VISA",
"status": "ACTIVE",
"zipCode": "12345",
"cardCategories": []
},
"default": true
}
}
]
}
}

Final Response β€” FAILED (HTTP 422)​

Sample Response β€” FAILED (HTTP 422)
{
"title": "REFUND_ERROR",
"detail": "Refund allocation processing failed for all records. Check individual records for error details",
"status": 422,
"refund": {
"id": "7dd80cba-910e-493f-9eb3-20f8324a1dcc",
"status": "FAILED",
"reason": "REQUESTED_BY_CUSTOMER",
"merchantTransactionId": "1f401160-e825-415f-81af-056fac57bba4",
"metadata": {},
"merchant": {
"id": "b955db5e-aef2-47de-bbb9-c80b9cc16e8f"
},
"refundAllocations": [
{
"id": "c4f347e5-a4fa-40de-883e-00cfa6352e31",
"amount": 99999999,
"status": "FAILED",
"error": {
"title": "REFUND_ERROR",
"detail": "The amount given exceeds the allowed refund limit"
},
"paymentMethod": {
"id": "09584dad-194e-455a-b980-0bb3abf10fe4",
"nickname": "string",
"paymentMethodDetails": {
"type": "CARD",
"last4": "4242",
"expiryMonth": 12,
"expiryYear": 2045,
"nameOnCard": "string",
"cardBrand": "VISA",
"status": "ACTIVE",
"zipCode": "12345",
"cardCategories": []
},
"default": true
}
}
]
}
}

Status Handling​

Polling Guidelines​

Refund processing is asynchronous β€” the API returns INITIATED immediately. Poll GET v2/refunds/{refundId} until the status settles to a terminal state (COMPLETED or FAILED).

  • βœ… Supported:
    • Poll the refund using refundId only.
    • Get real-time status in a single response.
    • See error details when refund fails.
  • ❌ Not Supported:
    • Polling by merchantTransactionId (not available).
warning

Always use the refund refundId for polling. Polling by merchantTransactionId is not supported and will result in API errors.


Status Definitions​

Unlinked refunds have simpler status transitions compared to split tender refunds since they process as a single operation.

Refund Status Definitions
StatusDescription
INITIATEDRefund request accepted; async validation and submission started
PENDINGRefund is being processed by the payment processor (Stripe)
COMPLETEDRefund processed successfully; funds returned to card
FAILEDRefund failed; no funds returned

Note: PENDING is optional β€” refunds may transition directly from INITIATED β†’ COMPLETED or INITIATED β†’ FAILED.


Status Lifecycle​

Refund Lifecycle​

Unlinked refunds move through these states:

Status Flow​


Error Handling​

Error Code Reference

For the complete list of error codes, status codes, and error details, see Refund Error Codes.

Unlinked Refund–Specific Errors​

Beyond standard refund errors, unlinked refunds have these specific failure modes:

  • CARD-only restriction: Payment method type must be CARD β€” ACH and bank accounts are rejected with 422 UNPROCESSABLE_ENTITY: Unlinked refunds are only supported for CARD payment methods.
  • Customer not found: Customer HSID does not exist in the system β†’ 422 UNPROCESSABLE_ENTITY: Customer not found.
  • Payment method not associated: Payment method does not belong to the specified customer β†’ 422 UNPROCESSABLE_ENTITY: Payment method not associated with customer.
  • Duplicate transaction: Same merchantTransactionId already used for an unlinked refund β†’ 422 UNPROCESSABLE_ENTITY: Duplicate merchant transaction.
  • Amount exceeds limit: Refund amount exceeds $10,000 USD β†’ 422 UNPROCESSABLE_ENTITY: Amount exceeds maximum allowed.
  • Invalid or expired card: Card is invalid, expired, or deleted β†’ Stripe rejects with failure details.

Validation Layers​

Unlinked refunds go through strict two-layer validation:

LayerError CodeExamples
Schema Validation400 INVALID_REQUESTMissing fields, invalid structure, wrong data types
Business Validation422 UNPROCESSABLE_ENTITYCARD-only violation, duplicate transaction, customer mismatch

Webhooks​

CCG sends one webhook per refund request. Subscribe to these events to get real-time refund outcomes without polling.

Webhook Events​

Webhook Spec
EventTriggerNotes
REFUND_SUCCESSRefund completed successfullyFunds returned to card
REFUND_FAILEDRefund failedNo funds returned
REFUND_PENDINGRefund queued (rare)Awaiting processor balance
Webhook Payload

Unlinked refund webhooks do not include a paymentId field since there's no associated payment. The payload includes:

  • refundId
  • merchantTransactionId
  • amount
  • status
  • vendorMerchantCustomerId
  • paymentMethod (optional β€” may not always be returned by Stripe)

Stripe(Payment Processor) Considerations​

  • Customer creation: CCG creates or finds the customer in Stripe before executing the refund
  • Refund execution: Stripe processes unlinked refunds via POST /v1/refunds without a charge parameter
  • Detection: Unlinked refunds detected via refund.charge == null in Stripe webhook events
  • Timing: Card refunds typically process in 1–3 business days
  • Balance requirements: If Stripe account has insufficient balance and autodebit is disabled, refund may be held in PENDING status

Retry Handling​

Unlinked refunds do not support automatic retries. Each refund request is a distinct operation:

  • Failed refund: If validation fails or processor rejects, merchant must handle manually:
    • Fix validation errors (e.g., use correct CARD payment method) and resubmit with new merchantTransactionId.
    • Verify customer and payment method association before retry.
    • Check payment method status (active, not expired, not deleted).
  • Duplicate prevention: Same merchantTransactionId cannot be reused β†’ always use a new unique ID for retry attempts.
  • Customer resolution: If customer not found, ensure customer exists in system before retry.

Recovery Steps for Failed Refunds​

SituationAction
Customer not foundVerify customer HSID and ensure customer exists in system
Payment method not associatedCheck payment method belongs to customer; update association
Invalid payment method typeUse CARD payment method only; ACH not supported
Expired or deleted cardUse alternate payment method or manual credit outside CCG
Amount exceeds limitReduce amount to $10,000 USD or less
Duplicate transaction IDGenerate new unique merchantTransactionId for retry
Stripe customer creation failedCheck Stripe account configuration; contact support if persists

V1 vs V2 API Comparison​

Unlinked Refund is supported in both v1 and v2 APIs, but v2 provides enhanced validation and clearer error handling. This section helps understand the differences and migrate from v1 to v2.

Key Differences​

AspectV1 API (POST /v1/refund)V2 API (POST /v2/refunds)
ValidationBasic validation onlyStrict two-layer validation (Schema + Business)
Error MessagesGeneric error responsesDetailed, actionable error codes
Customer RequiredYes (in request body)Yes (in request body with HSID)
Amount Limit$10,000 USD$10,000 USD
Duplicate DetectionLimitedStrict enforcement per merchant transaction ID
Webhook EventsSame event namesSame event names, better payload structure
StatusINITIATED, COMPLETED, FAILEDINITIATED, PENDING, COMPLETED, FAILED

Version-Specific Documentation​


FAQ​

Q: What's the difference between linked and unlinked refunds?

  • Linked refunds reference a paymentId and reverse a specific charge. Unlinked refunds use paymentMethodId and credit a card directly without a prior payment.

Q: Can I use unlinked refunds for ACH or bank accounts?

  • No. Unlinked refunds are CARD-only. ACH and bank accounts are not supported and will be rejected with 422 UNPROCESSABLE_ENTITY.

Q: Why is customer information required?

  • Customer validation ensures the payment method belongs to the intended recipient and provides proper audit trail and fraud prevention.

Q: Can I issue multiple unlinked refunds to the same card?

  • Yes, but each must use a unique merchantTransactionId. You cannot reuse the same transaction ID.

Q: What happens if the customer doesn't exist?

  • The request fails with 422 UNPROCESSABLE_ENTITY: Customer not found. Ensure the customer exists in the system before attempting an unlinked refund.

Q: What if the payment method is not associated with the customer?

  • The request fails with 422 UNPROCESSABLE_ENTITY: Payment method not associated with customer. Verify the association before retry.

Q: When is a refund status final?

  • COMPLETED and FAILED are terminal states. INITIATED and PENDING are transient.

Q: How do I correlate webhook events with refund records?

  • Use the refundId or merchantTransactionId in both the API response and webhook payload to match events.

Q: What if I don't receive a webhook?

  • Poll GET v2/refunds/{refundId} to get the current status. Check your endpoint logs and ensure it returns a 2xx status. See Webhook Configuration.

Q: Can I issue an unlinked refund exceeding $10,000?

  • No. The maximum amount per unlinked refund is $10,000 USD (1,000,000 cents). Requests exceeding this limit are rejected.

Integration Checklist​

  • Merchant account is configured and has access to refund APIs
  • API endpoints and authentication are set up
  • Refund request uses paymentMethodId (not paymentId) for unlinked refunds
  • Customer object includes required fields: firstName, lastName, hsid
  • Payment method is CARD type (not ACH or bank account)
  • Customer and payment method are properly associated in system
  • Each refund uses a unique merchantTransactionId
  • Amount does not exceed $10,000 USD per refund
  • Status polling uses refundId β€” not merchantTransactionId
  • Webhook endpoint is configured and returns 2xx
  • Webhook handler is idempotent and logs all payloads
  • All error codes and validation failures are handled
  • Integration tested with test cards and simulated failures