Enruta

Docs · Sellers

Adjustments

LiveUpdated

Sellers. Written for merchants that receive agent orders.

UCP, ACP, Adyen and Shopify each have an adjustments[] notification, and none of them executes anything or says what the refund does to the buyer’s limit. Enruta reads your adjustment, decides and executes the reversal on the buyer’s side, and gives you the same reversal rendered in all four shapes.

Report an adjustment

POST /v0/sellers/adjustments           Authorization: Bearer enruta_slr_…
{
  "domain": "reamandrule.com",
  "order_id": "ord_01M2512SM3V3A9R4EBJM8RRRV5",
  "protocol": "ucp",                                   // ucp | acp | shopify | adyen
  "adjustment": { "id": "adj_01M2G8Y2K5T8W3Q6N9B1D4H7XR", "type": "refund", "amount": { "value": "9.99", "currency": "USD" },
                  "reason": "customer_request", "occurred_at": "2026-09-13T15:03:40Z", "status": "completed" },
  "signature": "eyJhbGciOiJFUzI1NiIs…"                  // optional: your detached JWS over the adjustment
}

201 {
  "reversal": { "id": "rev_01M2G8YV5R3T7W2Q9K4N8B6DXP", "kind": "refund", "stage": "settled", "status": "completed", "mandate_effect": "none", "reversal_record_id": "rec_01M2G8Z0Q4W7T2K9N5B3D6H1XC" },
  "adjustments": {
    "ucp":     { "type": "refund", "amount": 999, "currency": "USD", "reason": "customer_request", "reference": "rev_01M2G8YV5R3T7W2Q9K4N8B6DXP" },
    "acp":     { "type": "refund", "amount": 999, "currency": "USD", "reason": "customer_request", "reference": "rev_01M2G8YV5R3T7W2Q9K4N8B6DXP" },
    "adyen":   { "pspReference": "rev_01M2G8YV5R3T7W2Q9K4N8B6DXP", "type": "refund", "status": "completed" },
    "shopify": { "kind": "refund", "amount": "9.99", "currency": "USD", "reason": "customer_request" }
  }
}

The reversal is created with initiator: merchant, kind mapped from your protocol’s type, protocol_ref and dedupe_key = "<order_id>:<adjustment id>", so reporting the same adjustment twice creates one reversal. POST /v0/sellers/reversals takes the same body with execute semantics: Enruta drives the buyer’s rail when it can (the sandbox rail at once, Stripe Issuing on test keys); a refund you already made in your own PSP stays pending until the buyer’s rail posts it or you report status: completed.

Or let the order webhook do it

If your store already sends the UCP order webhook to Enruta, an order_updated event whose order carries new entries in adjustments[] reports each of them, deduplicated by order_id:adjustment.id; an order_shipped event (or a shipped event in fulfillment.events[]) confirms fulfillment. The reference merchant’s admin “Refund” and “Ship” actions work exactly this way.

How types map

ProtocolIts `adjustments[].type` valuesMapping to `reversal.kind`
UCP6: refund, return, credit, price_adjustment, dispute, cancellationOne to one; disputechargeback_credit
ACP5One to one; chargeback_credit renders as dispute
Adyen Order API5, with status pending / completed / failedstatus maps to the reversal’s pending / completed / failed
Shopify8, of which 5 are reversals: refund, cancellation, return, exchange, order_editThose five map in; the three transaction-record types are not reversals

What happens on the buyer’s side

  • The reversal is decided against the buyer’s policy (amount within the original, stage, finality, window) and sealed as a linked record chained to the payment; when you signed the adjustment, the record’s subject is third_party_signed under your key.
  • The buyer’s mandate account and budget are refilled according to the buyer’s refill_policy; you see the mandate_effect in the answer but never the buyer’s cap.
  • The buyer’s agent is told through the reversal.completed webhook and the reversals[] on its payment request.
  • Your reconciliation slice gains an adjustments_leg entry with source: merchant_webhook; when the buyer’s rail posts the refund, a second entry with source: rail matches it.

Interfaces

SurfaceCall
REST, sellersPOST /v0/sellers/adjustments, POST /v0/sellers/reversals, POST /v0/sellers/fulfillments, POST /v0/ucp/orders (order_updated, order_shipped)
REST, buyer sideGET /v0/reversals/{id}/adjustments?protocol=ucp|acp|adyen|shopify
SDKsellers.adjustments.report(body), sellers.reversals.request(body), sellers.fulfillments.confirm(body)
Console (seller)/orders/{id}: the adjustments table with the delivery state; /reversals

Planned: adapters that read Adyen’s Order API and Shopify’s orders/updated for you (Adyen and Checkout.com January 2027; Shopify October 2026), so a refund made in your PSP’s dashboard becomes a reversal without a call from your code.