Orders API Full Integration Merchant Guide
This guide is for merchants who integrate their own backend directly with ChargebackStop using COMPLETE orders on a CUSTOM_ORDERS integration. It is designed for larger merchants with one organisation, multiple MIDs, and the full prevention suite enabled.
Use this guide when:
- You process payments through your own acquiring setup (one or more MIDs), rather than a processor ChargebackStop connects to directly.
- You want to power Ethoca Alerts, Verifi RDR, Visa Order Insight, Mastercard Consumer Clarity, Compelling Evidence 3.0 (CE3.0) and First-Party Trust (FPT) from a single order feed.
- You have (or will have) a live mode organisation and a test mode organisation.
If you already have a payment processor integration with ChargebackStop (Stripe, Adyen, etc.) and only need to enrich those transactions, use the Orders API partial enrichment merchant guide instead.
What you will build
Section titled “What you will build”| Component | What it does | ChargebackStop surface |
|---|---|---|
| Order feed | Sends every order (with transactions, items, deliveries, customer and device data) shortly after payment | POST /v1/orders/ |
| Lifecycle updates | Keeps orders current with refunds, disputes, delivery status and subscription changes | PATCH /v1/orders/{order_id} |
| Webhook consumer | Receives alert, enrolment and lookup events | Your HTTPS endpoint, configured in the dashboard |
| Alert resolution workflow | Refunds with your processor and resolves actionable alerts before the deadline | PATCH /v1/alerts/{alert_id} |
| Deflection monitoring | Tracks digital receipt lookups and CE3.0 deflection outcomes | lookup.* webhooks, GET /v1/lookups/ |
One order feed powers every tool:
| Tool | Network | What it does | How your order data is used |
|---|---|---|---|
| Ethoca Alerts | Mastercard (+ Visa via Ethoca) | Early dispute/fraud alerts you resolve by refunding | Transaction identifiers (ARN, auth code, BIN, last 4, amount) match alerts to your orders |
| Verifi RDR | Visa | Eligible disputes are automatically accepted and refunded at network level | Refund and dispute records reconcile RDR outcomes; rulesets control which cases refund |
| Consumer Clarity | Mastercard | Shows cardholders a rich digital receipt in their banking app | Order details, items, deliveries, refunds and merchant profile build the receipt |
| Order Insight | Visa | Shows cardholders purchase details at the point of dispute | Order details, items, delivery and payment information build the response |
| CE3.0 | Visa | Deflects disputes using the cardholder's history of legitimate purchases | customer_email plus device identifiers link purchases into qualifying history |
| First-Party Trust | Mastercard | Identifies legitimate transactions to reduce first-party fraud | customer_email, order_email and device identifiers |
How your account is structured
Section titled “How your account is structured”| Concept | ID prefix | Meaning in this integration |
|---|---|---|
| Organisation | org_ |
Your company. You have one live organisation and one test organisation. |
| Merchant | mrch_ |
One merchant per MID/CAID. An organisation with three MIDs has three merchants. |
| Integration | int_ |
Your CUSTOM_ORDERS order feed, linked to one or more merchants. |
| Enrolment | enrl_ |
A connection to one prevention programme (Ethoca Alerts, Verifi RDR, Consumer Clarity, Order Insight) for specific merchants. |
| Order | ord_ |
A record you create via the Orders API. |
| Alert | netalrt_ |
A network alert routed to your organisation via an enrolment. |
| Lookup | lkup_ |
A digital receipt or evidence request served from your order data. |
Multiple MIDs
Section titled “Multiple MIDs”Each MID (or CAID) is represented by one merchant. Programmes are enrolled per MID:
- Ethoca Alerts — enrolled by billing descriptor. Each MID's descriptors are registered on its enrolment.
- Verifi RDR — enrolled by Visa BIN + CAID (or ARNs). One enrolment per BIN/CAID pair.
- Consumer Clarity / First-Party Trust — enrolled per Mastercard merchant identity.
- Order Insight / CE3.0 — enrolled by Visa BIN + CAID.
We recommend a single CUSTOM_ORDERS integration linked to all of your merchants. Alerts and lookups are matched against orders from integrations whose merchants overlap the enrolment's merchants, so one integration spanning every MID keeps the feed simple and matching complete.
What ChargebackStop sets up, and what you build
Section titled “What ChargebackStop sets up, and what you build”During onboarding the ChargebackStop team provisions for you:
- Your live and test organisations.
- One merchant per MID, in each organisation.
- Your
CUSTOM_ORDERSintegration(s), linked to your merchants, with the validation rules for your enabled tools. - Enrolments for every programme: Ethoca Alerts (descriptors per MID), Verifi RDR (BIN + CAID per MID), Consumer Clarity (with First-Party Trust enabled) and Order Insight (with CE3.0 enabled).
You build:
- API keys (created in your dashboard) and the order feed.
- A webhook endpoint per environment.
- The alert resolution workflow (refund with your processor, then resolve the alert).
- Lifecycle updates (refunds, disputes, deliveries, subscriptions) into the Orders API.
Environments
Section titled “Environments”Both environments use the same base URL: https://api.chargebackstop.com.
| Test | Live | |
|---|---|---|
| Organisation | [TEST]-prefixed test mode organisation |
Live mode organisation |
| API key | Created in the test organisation's dashboard | Created in the live organisation's dashboard |
| Alerts and lookups | Generated with the Simulations API | Real network traffic |
| Webhooks | Test endpoint configured on the test organisation | Production endpoint configured on the live organisation |
Authentication
Section titled “Authentication”Create an API key in your dashboard under Settings → API keys, once per organisation (one test key, one live key). The key is displayed once — store it in your secrets manager immediately.
Every request uses the standard bearer scheme:
Authorization: Bearer <api_key>Content-Type: application/jsonOrganisation API keys created in the dashboard include the abilities this guide uses:
orders:read,orders:write,orders:updatealerts:read,alerts:writelookups:readintegrations:read,integrations:writerulesets:read,rulesets:writesimulations:alerts,simulations:enrollments,simulations:lookups,simulations:scheme_notices
Rate limits: 100 requests per minute per endpoint, per organisation. Each endpoint has an independent pool, and the limit applies to your organisation, not to the API key — creating extra keys does not raise it. A 429 response returns the code RATE_LIMITED with no Retry-After header, so use exponential backoff with jitter.
Step 1 — confirm your setup
Section titled “Step 1 — confirm your setup”Fetch your integration ID (you will send it with every order):
curl -X GET "https://api.chargebackstop.com/v1/integrations/?limit=20&offset=0" \ -H "Authorization: Bearer <api_key>"Find the integration with "type": "CUSTOM_ORDERS" in the response and store its id. Your organisation ID is shown in your dashboard and returned on every API object.
Store these IDs in your system's configuration:
| Your system | ChargebackStop ID | Used for |
|---|---|---|
| Environment config | organisation_id |
Every order, alert filter and simulation |
| Environment config | integration_id |
Every order |
| Per-MID mapping | merchant_id |
Interpreting which MID an alert or enrolment belongs to |
| Order record | order_id + your reference_id |
PATCH updates and reconciliation |
| Alert record | alert_id |
GET and PATCH on alerts |
Step 2 — set up webhooks
Section titled “Step 2 — set up webhooks”Configure your endpoint in the dashboard under Settings → Webhooks (do this in both organisations). Subscribe to:
alert.created,alert.updated— network alerts and their resolutionenrolment.created,enrolment.updated— programme enrolment status changeslookup.created,lookup.updated— digital receipt lookups and deflection outcomes
Your endpoint must be HTTPS and should return a 2xx within 20 seconds. Saving the endpoint generates a signing secret (whsec_...) — reveal it with the eye icon and store it with your API key.
Delivery contract
Section titled “Delivery contract”-
Payload:
{"id": "evt_dbXKdyUWLzSP98HMVdoFW","type": "alert.created","created_at": "2026-02-17T10:30:00Z","data": {"object": { "...": "entity snapshot" },"previous_attributes": { "...": "only on *.updated events" }},"api_version": "v1"}For alert events,
data.objectis the same alert object the Alerts API returns. Enrolment events useapi_version: "v2"and returnmerchant_idsas an array. -
Retries: if delivery fails, we retry up to 5 times over approximately two days (after 1 minute, 5 minutes, 30 minutes, 2 hours, then 12 hours). Retries of the same delivery keep the same
X-Idempotency-Keyheader. -
Deduplication: rely on the event
idfrom the payload to process each event exactly once. -
Health: endpoints that keep failing are automatically disabled and you are notified by email. Keep handlers fast — verify the signature, persist the event, enqueue processing, return
2xx. -
Fallback: poll
GET /v1/alerts/?status=ACTION_REQUIREDon a schedule so an outage on your side can never leave an actionable alert unseen.
Verify the signature
Section titled “Verify the signature”Every request includes an X-Signature header in the form t=<unix_timestamp>,v1=<hex_digest>, where the digest is an HMAC-SHA512 of "{timestamp}.{raw_body}" using your webhook secret. Reject requests whose timestamp is outside a small tolerance (for example five minutes) and whose signature does not match.
import crypto from 'crypto';
function verifySignature(rawBody, signatureHeader, secret) { const parts = Object.fromEntries( signatureHeader.split(',').map((p) => p.split('=')) ); const { t: timestamp, v1: receivedSig } = parts; if (!timestamp || !receivedSig) throw new Error('Malformed signature header');
const now = Math.floor(Date.now() / 1000); if (Math.abs(now - Number(timestamp)) > 5 * 60) { throw new Error('Timestamp outside allowed window'); }
const expectedSig = crypto .createHmac('sha512', secret) .update(`${timestamp}.${rawBody}`) .digest('hex');
const a = Buffer.from(receivedSig, 'hex'); const b = Buffer.from(expectedSig, 'hex'); if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) { throw new Error('Invalid signature'); } return true;}import hashlibimport hmacimport time
def verify_signature(raw_body: bytes, signature_header: str, secret: str) -> None: parts = dict(p.split("=", 1) for p in signature_header.split(",") if "=" in p) t, v1 = parts.get("t"), parts.get("v1") if not t or not v1: raise ValueError("Malformed signature header")
if abs(int(time.time()) - int(t)) > 5 * 60: raise ValueError("Timestamp outside allowed window")
payload = f'{t}.{raw_body.decode("utf-8")}'.encode("utf-8") expected = hmac.new(secret.encode(), payload, hashlib.sha512).hexdigest()
if not hmac.compare_digest(expected, v1): raise ValueError("Invalid signature")function verifySignature(string $rawBody, string $signatureHeader, string $secret): bool{ $parts = []; foreach (explode(',', $signatureHeader) as $part) { [$key, $val] = array_pad(explode('=', $part, 2), 2, null); $parts[$key] = $val; } $timestamp = $parts['t'] ?? null; $receivedSig = $parts['v1'] ?? null; if (!$timestamp || !$receivedSig) { throw new Exception('Malformed signature header'); } if (abs(time() - (int) $timestamp) > 5 * 60) { throw new Exception('Timestamp outside allowed window'); } $expectedSig = hash_hmac('sha512', $timestamp . '.' . $rawBody, $secret); if (!hash_equals($expectedSig, $receivedSig)) { throw new Exception('Invalid signature'); } return true;}Full delivery details, sample payloads for every event type, and a Postman collection are in the webhooks guide.
Step 3 — send every order
Section titled “Step 3 — send every order”Send each order to POST /v1/orders/ shortly after payment is captured. The request body is always a JSON array (even for one order), with up to 100 orders per request.
POST https://api.chargebackstop.com/v1/orders/Authorization: Bearer <api_key>Content-Type: application/jsonEvery order in a full integration uses "type": "COMPLETE" and must include:
| Field | Description |
|---|---|
type |
Must be "COMPLETE" |
organisation_id |
Your organisation ID |
integration_id |
Your CUSTOM_ORDERS integration ID |
reference_id |
Unique order identifier from your system |
order_datetime |
When the order was placed (ISO 8601) |
order_number |
Customer-facing order number |
order_subtotal_amount_in_cents |
Subtotal before tax |
order_currency |
ISO 4217 currency code |
order_total_amount_in_cents |
Total amount |
order_status |
OPEN_PENDING, OPEN_PENDING_RETURN, CLOSED_COMPLETE, CLOSED_CANCELLED or OTHER |
Field sets per tool
Section titled “Field sets per tool”One payload powers every tool. This table shows which fields each tool relies on beyond the required core above:
| Tool | Fields |
|---|---|
| Alert matching (Ethoca, RDR reconciliation) | At least one transaction with amount_in_cents, currency, authorised_at, payment_method_type, authorisation_status; for cards also payment_method_card_brand, payment_method_card_last_4, and at least one identifier: acquirer_reference_number, authorisation_code or payment_method_card_bin |
| Consumer Clarity | order_datetime, order_number, order_subtotal_amount_in_cents, order_currency, order_total_amount_in_cents; refund_datetime on every refund; receipt extras (items, deliveries, order_*_url links, merchant profile) make the receipt materially better |
| Order Insight | At least one items[] entry with name; keep order_number to 25 characters or fewer (longer values are truncated in Order Insight responses) |
| First-Party Trust | customer_email, order_email, and at least one of device_ip_address, device_id, device_fingerprint |
| CE3.0 | customer_email and at least one device identifier — on every order, so historical purchases qualify as evidence |
| Merchant information (multi-brand / per-MID receipts) | merchant_name, merchant_store_name, merchant_contact_phone, merchant_url when values differ per order; stable values can be configured as defaults in the platform instead |
Your integration is configured with validation rules for the tools you have enabled, so missing fields are rejected with a MISSING_FIELD error naming the field and the rule — you will find out at submission time, not at dispute time.
Matching identifiers. For card transactions, send all three identifiers whenever available. If you cannot, at least one of these combinations must be present:
acquirer_reference_number(works on its own — the strongest identifier)authorisation_code+payment_method_card_last_4payment_method_card_bin+payment_method_card_last_4+payment_method_card_brand+amount_in_cents+currency
Also send the transaction descriptor — with multiple MIDs, the billing descriptor confirms which MID's enrolment an alert belongs to.
Example: one order powering the full suite
Section titled “Example: one order powering the full suite”Full COMPLETE order payload
[ { "type": "COMPLETE", "organisation_id": "org_live123", "integration_id": "int_orders123", "reference_id": "txn-us1-100241",
"order_datetime": "2026-07-30T10:30:00Z", "order_number": "AUR-100241", "order_subtotal_amount_in_cents": 9000, "order_currency": "USD", "order_tax_amount_in_cents": 720, "order_total_amount_in_cents": 9720, "order_status": "CLOSED_COMPLETE", "order_phone": "+14155551234",
"order_view_url": "https://aurora.example.com/orders/AUR-100241", "order_request_refund_url": "https://aurora.example.com/orders/AUR-100241/refund", "order_proof_of_consent": "Customer accepted Terms of Service at checkout on 2026-07-30", "order_communications": "Order confirmation email sent 2026-07-30. Shipping notification sent 2026-07-31.",
"customer_email": "casey.jordan@example.com", "customer_first_name": "Casey", "customer_last_name": "Jordan", "customer_account_id": "cust-88712", "order_email": "casey.jordan@example.com",
"device_ip_address": "216.24.60.94", "device_id": "device-abc123", "device_fingerprint": "fp-xyz789",
"merchant_name": "Aurora Retail Group", "merchant_store_name": "Aurora US Store", "merchant_contact_phone": "+14155559876", "merchant_url": "https://aurora.example.com",
"transactions": [ { "reference_id": "txn-us1-100241", "amount_in_cents": 9720, "currency": "USD", "payment_method_type": "CARD", "authorisation_status": "SETTLED", "payment_method_reference_id": "pay-100241", "authorised_at": "2026-07-30T10:30:05Z", "descriptor": "AURORA US STORE", "acquirer_reference_number": "74027012345678901234567", "authorisation_code": "123456", "settlement_datetime": "2026-07-31T00:00:00Z", "cvc_verified": true, "three_d_secure_verified": true, "payment_method_card_brand": "VISA", "payment_method_card_last_4": "4242", "payment_method_card_bin": "424242", "billing_address": { "line_1": "123 Main St", "city": "New York", "country_subdivision": "NY", "postal_code": "10001", "country": "US" } } ],
"deliveries": [ { "reference_id": "dlv-100241-1", "type": "PHYSICAL", "physical_shipping_carrier": "UPS", "physical_shipping_tracking_number": "1Z999AA10123456784", "physical_shipping_status": "SHIPPED", "physical_shipping_datetime_shipped": "2026-07-31T08:00:00Z", "physical_shipping_address": { "line_1": "123 Main St", "city": "New York", "country_subdivision": "NY", "postal_code": "10001", "country": "US" } } ],
"items": [ { "reference_id": "item-100241-1", "name": "Trail Runner Pro", "price_in_cents": 9000, "quantity": 1, "sku": "TRP-001", "product_url": "https://aurora.example.com/products/trail-runner-pro", "delivery_reference_id": "dlv-100241-1" } ] }]For subscription MIDs, include a subscriptions entry (reference_id, interval, interval_price_in_cents, interval_currency, plus trial fields) and link items to it with subscription_reference_id. For digital goods, use a DIGITAL delivery with the digital_* field set — download timestamps and delivery IP are strong evidence.
The complete field reference — every field, type, validation rule and enum — is in the Orders API reference.
Handle the response
Section titled “Handle the response”POST /v1/orders/ uses partial success: each order in the batch is processed independently.
{ "created": 1, "failed": 1, "results": [ { "id": "ord_abc123", "reference_id": "txn-us1-100241", "...": "..." } ], "errors": [ { "index": 1, "reference_id": "txn-us1-100242", "code": "DUPLICATE_ORDER", "message": "Order with reference_id 'txn-us1-100242' already exists for this integration", "field": "reference_id" } ]}- Inspect
errors[]for every batch; route failures to a retry queue keyed byreference_id. - There is no idempotency header —
reference_idis your idempotency key. Retrying a request that already succeeded returnsDUPLICATE_ORDER(orDUPLICATE_REFERENCE_IDunder concurrent retries). Treat both as "already stored", not as failures. - A malformed request body (not an array, invalid types) returns a full-request
422and no orders are processed. - On
429or5xx, retry the same payload with exponential backoff — duplicates are rejected safely.
Step 4 — keep orders up to date
Section titled “Step 4 — keep orders up to date”Send lifecycle changes to PATCH /v1/orders/{order_id} as they happen. This is what keeps the receipt data current and lets us detect invalid alerts (for example an alert for a transaction you already refunded) before you spend money resolving them.
| When this happens in your system | Send |
|---|---|
| You issue a refund | A refunds entry (include refund_datetime — required for Consumer Clarity) |
| An item ships or is delivered | A deliveries update with the new physical_shipping_status and timestamps |
| A subscription is cancelled | A subscriptions update with status: "CANCELLED" |
| You receive a chargeback or inquiry directly | A disputes entry |
| The order is cancelled or returned | An order_status update |
curl -X PATCH "https://api.chargebackstop.com/v1/orders/ord_abc123" \ -H "Authorization: Bearer <api_key>" \ -H "Content-Type: application/json" \ -d '{ "refunds": [ { "reference_id": "refund-100241-1", "amount_in_cents": 9720, "currency": "USD", "status": "SUCCEEDED", "original_transaction_reference_id": "txn-us1-100241", "refund_datetime": "2026-08-02T12:00:00Z" } ] }'curl -X PATCH "https://api.chargebackstop.com/v1/orders/ord_abc123" \ -H "Authorization: Bearer <api_key>" \ -H "Content-Type: application/json" \ -d '{ "deliveries": [ { "reference_id": "dlv-100241-1", "physical_shipping_status": "DELIVERED", "physical_shipping_datetime_delivered": "2026-08-02T14:30:00Z" } ] }'curl -X PATCH "https://api.chargebackstop.com/v1/orders/ord_abc123" \ -H "Authorization: Bearer <api_key>" \ -H "Content-Type: application/json" \ -d '{ "subscriptions": [ { "reference_id": "sub-88712-1", "status": "CANCELLED", "cancellation_date": "2026-08-02T12:00:00Z" } ] }'curl -X PATCH "https://api.chargebackstop.com/v1/orders/ord_abc123" \ -H "Authorization: Bearer <api_key>" \ -H "Content-Type: application/json" \ -d '{ "disputes": [ { "reference_id": "dispute-100241-1", "amount_in_cents": 9720, "currency": "USD", "stage": "1ST_CHARGEBACK", "status": "OPEN", "type": "CHARGEBACK", "network_reason_code": "10.4", "payment_method_type": "CARD", "card_brand": "VISA" } ] }'Notes:
PATCHis all-or-nothing: if any part fails validation, the whole update is rolled back with a422.- Refunds and disputes are upserts by
reference_id; on existing recordsamount_in_centsandcurrency(and disputetype) are immutable. - Deliveries and subscriptions can only be updated if they were created with the order; delivery type cannot change.
Step 5 — respond to alerts
Section titled “Step 5 — respond to alerts”This is the operational core of the integration. Alert behaviour differs by programme:
| Enrolment type | How it arrives | What you must do |
|---|---|---|
ETHOCA_ALERT |
status: "ACTION_REQUIRED" with an action_required_deadline (typically around 48 hours) |
Refund with your processor, then resolve the alert via the API — or accept the dispute |
VERIFI_RDR |
status: "RESOLVED" with transaction_refund_outcome: "REFUNDED" — the refund already happened at network level |
Reconcile only: record the refund, stop fulfilment, cancel any subscription. Never refund again with your processor. |
The Ethoca alert workflow
Section titled “The Ethoca alert workflow”-
Receive the alert
Section titled “Receive the alert”Your webhook receives
alert.createdwithstatus: "ACTION_REQUIRED":{"id": "evt_dbXKdyUWLzSP98HMVdoFW","type": "alert.created","created_at": "2026-08-01T09:15:00Z","data": {"object": {"id": "netalrt_abc123","organisation_id": "org_live123","merchant_id": "mrch_usstore1","enrolment_id": "enrl_ethoca1","enrolment_type": "ETHOCA_ALERT","status": "ACTION_REQUIRED","action_required_deadline": "2026-08-03T09:15:00Z","transaction_amount_in_cents": 9720,"transaction_currency_code": "USD","transaction_authorised_at": "2026-07-30T10:30:05Z","transaction_authorisation_code": "123456","transaction_acquirer_reference_number": "74027012345678901234567","transaction_statement_descriptor": "AURORA US STORE","transaction_card_bin": "424242","transaction_card_last4": "4242","transaction_card_scheme": "VISA","transaction_refund_outcome": null,"integration_id": "int_orders123","integration_transaction_id": "txn-us1-100241"}},"api_version": "v1"}Acknowledge with a
2xximmediately and process asynchronously. -
Locate the order
Section titled “Locate the order”When the alert matched one of your transactions,
integration_transaction_idcontains your transactionreference_id— look the order up directly in your own system, or viaGET /v1/orders/?reference_id=<id>.merchant_idtells you which MID the alert belongs to.If
integration_transaction_idisnull, fall back to the transaction fields on the alert (ARN, auth code, card last 4, amount, descriptor). Persistent unmatched alerts usually mean gaps in your order feed — investigate them. -
Decide and act with your processor
Section titled “Decide and act with your processor”Apply your policy (most merchants refund fraud alerts below a value threshold and review the rest). If you decide to refund:
- Issue the refund through your payment processor for the alerted transaction.
- Stop fulfilment and cancel any related subscription in your own system.
ChargebackStop cannot do this step for you on a
CUSTOM_ORDERSintegration — the refund must happen in your systems. -
Resolve the alert before the deadline
Section titled “Resolve the alert before the deadline”Report the outcome so it reaches the issuer through Ethoca:
curl -X PATCH "https://api.chargebackstop.com/v1/alerts/netalrt_abc123" \-H "Authorization: Bearer <api_key>" \-H "Content-Type: application/json" \-d '{"action": "REFUND","note": "Refunded in full via processor, refund id re_9k2..."}'Action Meaning for a Custom Orders integration REFUNDDeclares you have refunded the customer with your processor. Resolves the alert with transaction_refund_outcome: "REFUNDED". It does not move money.ACCEPT_DISPUTEYou are not refunding; the dispute proceeds and you may fight it. Resolves with transaction_refund_outcome: "NOT_REFUNDED".(
CANCELandREFUND_AND_CANCELexist for integrations where ChargebackStop has processor or subscription access; with Custom Orders useREFUNDorACCEPT_DISPUTE.)The response returns the alert with
status: "RESOLVED". Resolved alerts cannot be actioned again (422 INVALID_ACTION). -
Update the order
Section titled “Update the order”Record the refund on the order too, so future alerts and receipts reflect it:
curl -X PATCH "https://api.chargebackstop.com/v1/orders/ord_abc123" \-H "Authorization: Bearer <api_key>" \-H "Content-Type: application/json" \-d '{"refunds": [{"reference_id": "refund-100241-1","amount_in_cents": 9720,"currency": "USD","status": "SUCCEEDED","original_transaction_reference_id": "txn-us1-100241","refund_datetime": "2026-08-01T10:05:00Z"}]}'
You will also receive alert.updated events whenever status, transaction_refund_outcome or subscription_cancel_outcome change — including for resolutions made by your own team in the dashboard, and for RDR alerts. Alerts with status: "INVALID" were withdrawn or detected as invalid (for example, already refunded before the alert) — store them for reporting; no action is needed.
Step 6 — monitor deflection outcomes
Section titled “Step 6 — monitor deflection outcomes”Consumer Clarity, Order Insight, FPT and CE3.0 activity is visible as lookups — one record per time a network requested your order data:
lookup.createdfires when an issuer/cardholder triggers a lookup against your enrolments.lookup.updatedfires whenlookup_statusordeflection_statuschanges.
Key fields on the lookup object:
| Field | Values | Meaning |
|---|---|---|
type |
ETHOCA_CONSUMER_CLARITY, ETHOCA_FIRST_PARTY_TRUST, VERIFI_ORDER_INSIGHT, VERIFI_COMPELLING_EVIDENCE_3 |
Which product served the request |
lookup_status |
PENDING, SUCCEEDED, FAILED, TIMEOUT |
Whether we answered the network successfully |
deflection_status |
NOT_ATTEMPTED, PENDING, SUCCEEDED, FAILED |
Whether the dispute was deflected (CE3.0) |
integration_transaction_id |
Your transaction reference_id |
Ties the lookup back to your order |
Use deflection_status: "SUCCEEDED" as the positive CE3.0 deflection signal for reporting. A high rate of lookups with integration_transaction_id: null means the networks are asking about transactions your order feed doesn't cover — the fix is always more complete order data. You can also query history with GET /v1/lookups/ (Lookups API reference).
Optional — control RDR decisioning with rulesets
Section titled “Optional — control RDR decisioning with rulesets”By default, RDR accepts and refunds every eligible Visa dispute on your enrolled BIN/CAIDs. If you want exceptions — for example, contest disputes above a value threshold — create a ruleset with the Rulesets API (your organisation key has rulesets:read / rulesets:write):
curl -X POST "https://api.chargebackstop.com/v1/rulesets/" \ -H "Authorization: Bearer <api_key>" \ -H "Content-Type: application/json" \ -d '{ "organisation_id": "org_live123", "enrolment_ids": ["enrl_rdr_us1"], "outcome": "ACCEPT_DISPUTE", "join_operator": "OR", "rules": [ { "type": "AMOUNT", "parameters": { "operator": "GREATER_THAN", "currency_code": "USD", "amount_in_cents": 25000 } } ] }'This example declines the automatic refund for disputes over $250, letting them proceed as normal disputes you can fight. Create rulesets before go-live if you need them — see the Rulesets API reference and resolution rules for details.
Test the integration end to end
Section titled “Test the integration end to end”Everything above works identically in your test organisation, with simulated network traffic. Use your test organisation's API key and IDs throughout.
-
Enable your test enrolments
Section titled “Enable your test enrolments”Test enrolments are not activated by the real networks, so enable them via simulation and confirm the
enrolment.updatedwebhook arrives:curl -X PATCH "https://api.chargebackstop.com/v1/simulate/enrolments/enrl_test456" \-H "Authorization: Bearer <api_key>" \-H "Content-Type: application/json" \-d '{"status": "ENABLED"}' -
Submit a test order
Section titled “Submit a test order”Send a
COMPLETEorder to your test integration with a known ARN (exactly 23 characters) and authorisation code (exactly 6 characters) on the transaction. -
Simulate an actionable Ethoca alert
Section titled “Simulate an actionable Ethoca alert”curl -X POST "https://api.chargebackstop.com/v1/simulate/alerts" \-H "Authorization: Bearer <api_key>" \-H "Content-Type: application/json" \-d '{"organisation_id": "org_test123","enrolment_id": "enrl_test456","status": "ACTION_REQUIRED","card_scheme": "VISA","amount_in_cents": 9720,"currency_code": "USD","transaction_acquirer_reference_number": "74027012345678901234567","transaction_authorisation_code": "123456"}'This fires a real
alert.createdwebhook to your test endpoint. Note: the simulator does not run order matching inline, sointegration_transaction_idmay benull— exercise your fallback matching path here, and verify matched alerts against real traffic during go-live. -
Resolve it
Section titled “Resolve it”Run your full workflow: webhook → order lookup → (pretend) processor refund →
PATCH /v1/alerts/{alert_id}with"action": "REFUND"→PATCHthe order with the refund. Confirm thealert.updatedwebhook showsstatus: "RESOLVED"andtransaction_refund_outcome: "REFUNDED". -
Simulate the rest of the suite
Section titled “Simulate the rest of the suite”- An RDR alert (RDR only supports
RESOLVEDorINVALID): confirm your handler records it without triggering a processor refund. - A lookup via
POST /v1/simulate/lookupsfor eachtypeyou use: confirmlookup.createdhandling and your deflection reporting.
See the Simulations API reference for all options.
- An RDR alert (RDR only supports
Sandbox acceptance checklist
Section titled “Sandbox acceptance checklist”- Test API key stored in secrets manager; never used against live IDs
- Webhook endpoint verifies
X-Signatureand rejects bad/stale signatures - Webhook processing is idempotent by event
idand returns2xxwithin 20 seconds -
COMPLETEorders submit successfully with all enabled field sets (matching, CC, OI, FPT, CE3.0) - Batch errors (
errors[]) are inspected and retried;DUPLICATE_ORDERtreated as success - Refund/delivery/subscription/dispute updates flow via
PATCH /v1/orders/{order_id} - Simulated
ACTION_REQUIREDalert resolved withREFUNDbefore deadline - Simulated alert resolved with
ACCEPT_DISPUTE(no-refund path) - Simulated RDR alert reconciled without a processor refund
- Simulated lookups processed; deflection reporting uses
deflection_status - Polling fallback on
GET /v1/alerts/?status=ACTION_REQUIREDworks - Client handles
401,403,404,422,429and5xxwith backoff
Go-live checklist
Section titled “Go-live checklist”-
Create live credentials and webhooks
Section titled “Create live credentials and webhooks”Create the live organisation API key and configure the production webhook endpoint on the live organisation; store both secrets separately from test.
-
Backfill order history
Section titled “Backfill order history”Backfill order history (12 months where possible) into the live integration before alert traffic is enabled.
-
Enable real-time feeds
Section titled “Enable real-time feeds”Switch on the real-time order feed and lifecycle updates.
-
Confirm enrolments are enabled
Section titled “Confirm enrolments are enabled”Confirm every enrolment reports
status: "ENABLED"(viaenrolment.updatedwebhooks or your dashboard) before treating a programme as live. -
Verify alert matching
Section titled “Verify alert matching”Verify the first live alerts arrive with
integration_transaction_idpopulated — unmatched alerts at go-live mean feed gaps. -
Cover the Ethoca deadline
Section titled “Cover the Ethoca deadline”Confirm your operations rota covers the Ethoca deadline window (including weekends) and that deadline alerts page a human.
-
Verify refund workflows
Section titled “Verify refund workflows”Verify the first refunds flow end to end: processor refund → alert resolved → order updated.
-
Monitor lookups
Section titled “Monitor lookups”Monitor lookups in the first weeks:
lookup_status: "SUCCEEDED"with your orders attached, and CE3.0deflection_statusoutcomes.
Error handling reference
Section titled “Error handling reference”All APIs use the same error envelope:
{ "errors": [ { "code": "ERROR_CODE", "message": "Human-readable message" } ]}| Situation | Code | Handling |
|---|---|---|
| Missing/invalid API key | UNAUTHORISED (401) |
Check the key and the environment it belongs to |
| Key lacks an ability | FORBIDDEN (403) |
Recreate the key in the dashboard |
| Rate limited | RATE_LIMITED (429) |
Exponential backoff with jitter; no Retry-After header is sent |
| Order already exists | DUPLICATE_ORDER / DUPLICATE_REFERENCE_ID (in errors[]) |
Treat as already stored |
| Some orders in the batch failed | Per-order codes in errors[] (200) |
Check errors[] on every batch; a 200 alone does not mean success |
| Every order in the batch failed | Per-order codes in errors[] (400) |
Same body shape as 200; nothing was created |
| Wrong order type for integration | INVALID_ORDER_TYPE |
COMPLETE orders require the CUSTOM_ORDERS integration |
| Integration not found/accessible | INVALID_INTEGRATION |
Check integration_id and organisation_id |
| Field required by your enabled tools | MISSING_FIELD |
The message names the field and the rule (e.g. visa_compelling_evidence_3_0) |
| Whole-request schema failure | VALIDATION_* (422) |
Fix payload shape/types; nothing was processed |
| Actioning a resolved alert | INVALID_ACTION (422) |
Fetch the latest alert state before actioning |
| Simulating against a live org | SIMULATION_NOT_ALLOWED (422) |
Simulations work only on test organisations |
One inconsistency to code around: PATCH /v1/orders/{order_id} returns 404 as {"detail": "Order with ID ord_x not found"} rather than the standard errors[] envelope.
Related documentation
Section titled “Related documentation”- Orders API reference — every field, validation rule and error code
- Alerts API reference
- Lookups API reference
- Simulations API reference
- Webhooks guide — payloads for all event types and a Postman collection
- Transaction matching
- Ethoca Alerts and Verifi RDR