Skip to content
Dashboard
DeveloperMerchant Integration Guides

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.

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
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.

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_ORDERS integration(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.

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

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/json

Organisation API keys created in the dashboard include the abilities this guide uses:

  • orders:read, orders:write, orders:update
  • alerts:read, alerts:write
  • lookups:read
  • integrations:read, integrations:write
  • rulesets:read, rulesets:write
  • simulations: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.

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

Configure your endpoint in the dashboard under Settings → Webhooks (do this in both organisations). Subscribe to:

  • alert.created, alert.updated — network alerts and their resolution
  • enrolment.created, enrolment.updated — programme enrolment status changes
  • lookup.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.

  • 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.object is the same alert object the Alerts API returns. Enrolment events use api_version: "v2" and return merchant_ids as 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-Key header.

  • Deduplication: rely on the event id from 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_REQUIRED on a schedule so an outage on your side can never leave an actionable alert unseen.

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;
}

Full delivery details, sample payloads for every event type, and a Postman collection are in the webhooks guide.

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/json

Every 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

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_4
  • payment_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.

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 by reference_id.
  • There is no idempotency header — reference_id is your idempotency key. Retrying a request that already succeeded returns DUPLICATE_ORDER (or DUPLICATE_REFERENCE_ID under concurrent retries). Treat both as "already stored", not as failures.
  • A malformed request body (not an array, invalid types) returns a full-request 422 and no orders are processed.
  • On 429 or 5xx, retry the same payload with exponential backoff — duplicates are rejected safely.

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"
}
]
}'

Notes:

  • PATCH is all-or-nothing: if any part fails validation, the whole update is rolled back with a 422.
  • Refunds and disputes are upserts by reference_id; on existing records amount_in_cents and currency (and dispute type) are immutable.
  • Deliveries and subscriptions can only be updated if they were created with the order; delivery type cannot change.

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.
  1. Your webhook receives alert.created with status: "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 2xx immediately and process asynchronously.

  2. When the alert matched one of your transactions, integration_transaction_id contains your transaction reference_id — look the order up directly in your own system, or via GET /v1/orders/?reference_id=<id>. merchant_id tells you which MID the alert belongs to.

    If integration_transaction_id is null, 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.

  3. 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_ORDERS integration — the refund must happen in your systems.

  4. 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
    REFUND Declares you have refunded the customer with your processor. Resolves the alert with transaction_refund_outcome: "REFUNDED". It does not move money.
    ACCEPT_DISPUTE You are not refunding; the dispute proceeds and you may fight it. Resolves with transaction_refund_outcome: "NOT_REFUNDED".

    (CANCEL and REFUND_AND_CANCEL exist for integrations where ChargebackStop has processor or subscription access; with Custom Orders use REFUND or ACCEPT_DISPUTE.)

    The response returns the alert with status: "RESOLVED". Resolved alerts cannot be actioned again (422 INVALID_ACTION).

  5. 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.

Consumer Clarity, Order Insight, FPT and CE3.0 activity is visible as lookups — one record per time a network requested your order data:

  • lookup.created fires when an issuer/cardholder triggers a lookup against your enrolments.
  • lookup.updated fires when lookup_status or deflection_status changes.

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.

Everything above works identically in your test organisation, with simulated network traffic. Use your test organisation's API key and IDs throughout.

  1. Test enrolments are not activated by the real networks, so enable them via simulation and confirm the enrolment.updated webhook 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"}'
  2. Send a COMPLETE order to your test integration with a known ARN (exactly 23 characters) and authorisation code (exactly 6 characters) on the transaction.

  3. 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.created webhook to your test endpoint. Note: the simulator does not run order matching inline, so integration_transaction_id may be null — exercise your fallback matching path here, and verify matched alerts against real traffic during go-live.

  4. Run your full workflow: webhook → order lookup → (pretend) processor refund → PATCH /v1/alerts/{alert_id} with "action": "REFUND" → PATCH the order with the refund. Confirm the alert.updated webhook shows status: "RESOLVED" and transaction_refund_outcome: "REFUNDED".

    • An RDR alert (RDR only supports RESOLVED or INVALID): confirm your handler records it without triggering a processor refund.
    • A lookup via POST /v1/simulate/lookups for each type you use: confirm lookup.created handling and your deflection reporting.

    See the Simulations API reference for all options.

  • Test API key stored in secrets manager; never used against live IDs
  • Webhook endpoint verifies X-Signature and rejects bad/stale signatures
  • Webhook processing is idempotent by event id and returns 2xx within 20 seconds
  • COMPLETE orders submit successfully with all enabled field sets (matching, CC, OI, FPT, CE3.0)
  • Batch errors (errors[]) are inspected and retried; DUPLICATE_ORDER treated as success
  • Refund/delivery/subscription/dispute updates flow via PATCH /v1/orders/{order_id}
  • Simulated ACTION_REQUIRED alert resolved with REFUND before 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_REQUIRED works
  • Client handles 401, 403, 404, 422, 429 and 5xx with backoff
  1. Create the live organisation API key and configure the production webhook endpoint on the live organisation; store both secrets separately from test.

  2. Backfill order history (12 months where possible) into the live integration before alert traffic is enabled.

  3. Switch on the real-time order feed and lifecycle updates.

  4. Confirm every enrolment reports status: "ENABLED" (via enrolment.updated webhooks or your dashboard) before treating a programme as live.

  5. Verify the first live alerts arrive with integration_transaction_id populated — unmatched alerts at go-live mean feed gaps.

  6. Confirm your operations rota covers the Ethoca deadline window (including weekends) and that deadline alerts page a human.

  7. Verify the first refunds flow end to end: processor refund → alert resolved → order updated.

  8. Monitor lookups in the first weeks: lookup_status: "SUCCEEDED" with your orders attached, and CE3.0 deflection_status outcomes.

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.