For the complete documentation index, see llms.txt. This page is also available as Markdown.

API integration guide

Full API documentation is available here: https://api.chargebackstop.com/v1/docs.

V2 API documentation (enrolments endpoint only for now): https://api.chargebackstop.com/v2/docs.

Onboarding a new customer into your partner account

1

Create an organisation

Use the /v1/organisations endpoint. Save the organisation ID for later use in the memberships and merchants APIs.

Sample request:

curl -X POST "https://api.chargebackstop.com/v1/organisations/" \
  -H "Authorization: Bearer <partner_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Fitness"
  }'

Sample response:

{
  "id": "org_abc123",
  "name": "Acme Fitness",
  "mode": "LIVE",
  "created_at": "2026-02-17T10:00:00Z",
  "updated_at": "2026-02-17T10:00:00Z"
}

Save organisation_id = org_abc123. You will use this in memberships, merchants, and integrations requests.

2

Add your customer users to their organisation

Use the /v1/memberships endpoint.

You can skip this step if you do not intend for your users to log in to ChargebackStop.

Sample request:

curl -X POST "https://api.chargebackstop.com/v1/memberships/" \
  -H "Authorization: Bearer <partner_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "organisation_id": "org_abc123",
    "first_name": "Casey",
    "last_name": "Jordan",
    "email": "casey.jordan@acmefitness.com",
    "role": "ADMIN",
    "is_alert_action_required_email_enabled": true,
    "is_alert_resolved_email_enabled": true,
    "is_alert_dismissed_email_enabled": true,
    "is_evidence_requested_email_enabled": true
  }'

Sample response:

{
  "id": "mem_abc123",
  "organisation_id": "org_abc123",
  "user_id": "usr_abc123",
  "first_name": "Casey",
  "last_name": "Jordan",
  "email": "casey.jordan@acmefitness.com",
  "role": "ADMIN",
  "is_alert_action_required_email_enabled": true,
  "is_alert_resolved_email_enabled": true,
  "is_alert_dismissed_email_enabled": true,
  "is_evidence_requested_email_enabled": true,
  "created_at": "2026-02-17T10:01:00Z",
  "updated_at": "2026-02-17T10:01:00Z"
}

This creates the customer user and links them to the organisation.

3

Add their merchant accounts into the newly created organisation

Use the /v1/merchants endpoint. Save the merchant account IDs for later use in the integrations and enrolments APIs.

Sample request:

curl -X POST "https://api.chargebackstop.com/v1/merchants/" \
  -H "Authorization: Bearer <partner_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "organisation_id": "org_abc123",
    "name": "Acme Fitness Main Store",
    "type": "GENERIC",
    "external_id": "acct_1ABCDEF2345678"
  }'

Sample response:

{
  "id": "mrch_abc123",
  "organisation_id": "org_abc123",
  "name": "Acme Fitness Main Store",
  "type": "GENERIC",
  "external_id": "acct_1ABCDEF2345678",
  "default_representment_service_type": null,
  "created_at": "2026-02-17T10:02:00Z",
  "updated_at": "2026-02-17T10:02:00Z"
}

Save merchant_id = mrch_abc123. You will use this in integrations and enrolments requests.

4

If you intend to use the Orders API, create an integration

Use the /v1/integrations endpoint. Save the integration ID for later use in the orders API.

Sample request:

curl -X POST "https://api.chargebackstop.com/v1/integrations/" \
  -H "Authorization: Bearer <partner_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "organisation_id": "org_abc123",
    "name": "Acme Fitness Orders",
    "type": "CUSTOM_ORDERS",
    "status": "ENABLED",
    "merchant_ids": ["mrch_abc123"]
  }'

Sample response:

{
  "id": "int_abc123",
  "organisation_id": "org_abc123",
  "name": "Acme Fitness Orders",
  "type": "CUSTOM_ORDERS",
  "status": "ENABLED",
  "merchant_ids": ["mrch_abc123"]
}

Save integration_id = int_abc123. You will use this for orders submissions.

5

If you wish to enrol the customer with Ethoca or Verifi RDR, create enrolments

Use the /v2/enrolments endpoint. Verifi RDR and Ethoca enrolments are separate; create both if the customer wants both services.

See Enrolments API docs.

Create Ethoca enrolment

Sample request:

curl -X POST "https://api.chargebackstop.com/v2/enrolments/" \
  -H "Authorization: Bearer <partner_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_ids": ["mrch_abc123"],
    "type": "ETHOCA_ALERT",
    "ethoca_alert": {
      "descriptors": [
        {
          "descriptor": "ACMEFIT",
          "match_type": "STARTS_WITH"
        }
      ]
    }
  }'

Sample response:

{
  "id": "enrl_ethoca123",
  "organisation_id": "org_abc123",
  "merchant_ids": ["mrch_abc123"],
  "type": "ETHOCA_ALERT",
  "status": "PENDING",
  "ethoca_alert": {
    "descriptors": [
      {
        "descriptor": "ACMEFIT",
        "match_type": "STARTS_WITH"
      }
    ]
  },
  "created_at": "2026-02-17T10:03:00Z",
  "updated_at": "2026-02-17T10:03:00Z",
  "note": null
}

Save ethoca_enrolment_id = enrl_ethoca123 if you need to track this enrolment status.

Create Verifi RDR enrolment

Sample request:

curl -X POST "https://api.chargebackstop.com/v2/enrolments/" \
  -H "Authorization: Bearer <partner_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_ids": ["mrch_abc123"],
    "type": "VERIFI_RDR",
    "verifi_rdr": {
      "bin": "424242",
      "caid": "ABC1234567"
    }
  }'

You must provide either both bin and caid, or at least one ARN in the arns array. For example, to enrol using ARNs instead:

{
  "merchant_ids": ["mrch_abc123"],
  "type": "VERIFI_RDR",
  "verifi_rdr": {
    "arns": ["74027012345678901234567"]
  }
}

Sample response:

{
  "id": "enrl_verifi123",
  "organisation_id": "org_abc123",
  "merchant_ids": ["mrch_abc123"],
  "type": "VERIFI_RDR",
  "status": "PENDING",
  "verifi_rdr": {
    "bin": "424242",
    "caid": "ABC1234567"
  },
  "created_at": "2026-02-17T10:04:00Z",
  "updated_at": "2026-02-17T10:04:00Z",
  "note": null
}

Save verifi_enrolment_id = enrl_verifi123 if you need to track this enrolment status.

Note: Once the enrolment is enabled (2-3 business days for Ethoca, and up to 5-14 business days for Verifi RDR), your customer will start receiving pre-dispute alerts in their organisation.

6

Optional: Configure resolution rules for the new enrolments

Use the /v1/rulesets endpoint to control what happens when a pre-dispute alert matches a transaction. A ruleset is scoped to one or more enrolments of the same type — you cannot combine Ethoca and Verifi RDR enrolments in the same ruleset. If you need the same logic for both, create one ruleset per enrolment type.

See Rulesets API docs.

Verifi RDR

By default, every Verifi RDR alert results in a refund of the matching transaction. Verifi RDR alerts are resolved at the network level, so they arrive already resolved and have no ACTION_REQUIRED state — they are not actionable after the fact.

If you want to limit which transactions are refunded — for example, never auto-refund transactions above a certain amount — create a ruleset with an ACCEPT_DISPUTE outcome and add rules describing the transactions that should bypass the default refund.

Sample request — accept dispute (skip auto-refund) for any Verifi RDR transaction above $50:

curl -X POST "https://api.chargebackstop.com/v1/rulesets/" \
  -H "Authorization: Bearer <partner_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "organisation_id": "org_abc123",
    "enrolment_ids": ["enrl_verifi123"],
    "outcome": "ACCEPT_DISPUTE",
    "join_operator": "AND",
    "rules": [
      {
        "type": "AMOUNT",
        "parameters": {
          "operator": "GREATER_THAN",
          "currency_code": "USD",
          "amount_in_cents": 5000
        }
      }
    ]
  }'

Ethoca

Resolution rules for Ethoca alerts only make sense if you have a payment processor integration in place (e.g. Stripe, Adyen) and ChargebackStop is matching incoming alerts to transactions. Ethoca alerts are actioned by issuing a refund through the original payment processor, so without a matched transaction there is nothing for a rule to act on.

Once matching is in place, a ruleset can auto-action matched Ethoca alerts that meet your criteria, so the user does not have to handle them manually.

Sample request — automatically refund and cancel matched Ethoca alerts whose descriptor starts with ACMEFIT:

curl -X POST "https://api.chargebackstop.com/v1/rulesets/" \
  -H "Authorization: Bearer <partner_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "organisation_id": "org_abc123",
    "enrolment_ids": ["enrl_ethoca123"],
    "outcome": "REFUND_AND_CANCEL",
    "join_operator": "AND",
    "rules": [
      {
        "type": "DESCRIPTOR",
        "parameters": {
          "descriptors": [
            {"value": "ACMEFIT", "match_type": "STARTS_WITH"}
          ]
        }
      }
    ]
  }'
7

Start pushing orders to us using the Orders API

Use the /v1/orders endpoint.

Send a COMPLETE order that satisfies alert matching requirements. COMPLETE orders can be sent directly to your CUSTOM_ORDERS integration.

Sample request:

curl -X POST "https://api.chargebackstop.com/v1/orders/" \
  -H "Authorization: Bearer <partner_api_key>" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "type": "COMPLETE",
      "organisation_id": "org_abc123",
      "integration_id": "int_abc123",
      "reference_id": "order-10001",
      "order_datetime": "2026-02-17T09:59:00Z",
      "order_number": "AF-10001",
      "order_subtotal_amount_in_cents": 4900,
      "order_currency": "USD",
      "order_total_amount_in_cents": 4900,
      "order_status": "CLOSED_COMPLETE",
      "customer_email": "casey.jordan@acmefitness.com",
      "transactions": [
        {
          "reference_id": "txn-10001",
          "amount_in_cents": 4900,
          "currency": "USD",
          "payment_method_type": "CARD",
          "authorisation_status": "SETTLED",
          "payment_method_reference_id": "pi_3QJ2ABCD12345678",
          "authorised_at": "2026-02-17T09:59:05Z",
          "payment_method_card_brand": "VISA",
          "payment_method_card_last_4": "4242",
          "acquirer_reference_number": "74027012345678901234567"
        }
      ]
    }
  ]'

Sample response:

{
  "created": 1,
  "failed": 0,
  "results": [
    {
      "id": "ord_abc123",
      "reference_id": "order-10001",
      "type": "COMPLETE",
      "organisation_id": "org_abc123",
      "integration_id": "int_abc123",
      "created_at": "2026-02-17T10:05:00Z",
      "transactions": [
        {
          "reference_id": "txn-10001",
          "amount_in_cents": 4900,
          "currency": "USD",
          "payment_method_type": "CARD",
          "authorisation_status": "SETTLED",
          "payment_method_card_brand": "VISA",
          "payment_method_card_last_4": "4242",
          "acquirer_reference_number": "74027012345678901234567"
        }
      ]
    }
  ],
  "errors": []
}

This creates a COMPLETE order with the core card transaction fields required for alert matching.

8

Optional: Run this flow with a Node.js script

If you want to run the same onboarding flow end-to-end and print all created IDs automatically, use:

node onboarding-flow.js

Before running it, save the script below as onboarding-flow.js and set API_KEY.

Full onboarding flow script (Node.js):

/*
 * Simple onboarding flow example for partner API keys.
 *
 * Update the variables in the CONFIG section, then run:
 * node onboarding-flow.js
 */

// -----------------------------------------------------------------------------
// CONFIG
// -----------------------------------------------------------------------------
const API_BASE_URL = "https://api.chargebackstop.com";
const API_KEY = "REPLACE_WITH_PARTNER_API_KEY";

const MEMBER_USER_EMAIL = "test@example.com";
const MEMBER_FIRST_NAME = "Casey";
const MEMBER_LAST_NAME = "Jordan";

const ORGANISATION_NAME = "Acme Fitness";
const MERCHANT_NAME = "Acme Fitness Main Store";
const MERCHANT_TYPE = "GENERIC";
const MERCHANT_EXTERNAL_ID = "acct_1ABCDEF2345678";

const INTEGRATION_NAME = "Acme Fitness Orders";

const ORDER_REFERENCE_ID = "order-10001";
const ORDER_NUMBER = "AF-10001";
const ORDER_CUSTOMER_EMAIL = MEMBER_USER_EMAIL;
const ORDER_TRANSACTION_REFERENCE_ID = "txn-10001";

// -----------------------------------------------------------------------------
// HELPERS
// -----------------------------------------------------------------------------
function fail(message, details) {
  console.error(`\nERROR: ${message}`);
  if (details !== undefined) {
    console.error(JSON.stringify(details, null, 2));
  }
  process.exit(1);
}

function logStep(stepNumber, title) {
  console.log(`\n[Step ${stepNumber}] ${title}`);
}

function printJson(label, data) {
  console.log(`${label}:`);
  console.log(JSON.stringify(data, null, 2));
}

async function apiRequest({ method, path, body }) {
  const response = await fetch(`${API_BASE_URL}${path}`, {
    method,
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: body ? JSON.stringify(body) : undefined,
  });

  const rawText = await response.text();
  let data = null;

  if (rawText.length > 0) {
    try {
      data = JSON.parse(rawText);
    } catch (error) {
      data = { raw: rawText };
    }
  }

  if (!response.ok) {
    fail(`Request failed: ${method} ${path} (${response.status})`, data);
  }

  return data;
}

// -----------------------------------------------------------------------------
// FLOW
// -----------------------------------------------------------------------------
async function main() {
  if (API_KEY.includes("REPLACE_WITH_PARTNER_API_KEY")) {
    fail("Set API_KEY at the top of this file before running the script.");
  }

  const createdIds = {
    organisation_id: null,
    membership_id: null,
    merchant_id: null,
    integration_id: null,
    ethoca_enrolment_id: null,
    verifi_enrolment_id: null,
    order_id: null,
  };

  logStep(1, "Create organisation");
  const organisation = await apiRequest({
    method: "POST",
    path: "/v1/organisations/",
    body: {
      name: ORGANISATION_NAME,
    },
  });
  createdIds.organisation_id = organisation.id;
  printJson("Response", organisation);
  console.log(`Saved organisation_id: ${createdIds.organisation_id}`);

  logStep(2, "Create membership");
  const membership = await apiRequest({
    method: "POST",
    path: "/v1/memberships/",
    body: {
      organisation_id: createdIds.organisation_id,
      first_name: MEMBER_FIRST_NAME,
      last_name: MEMBER_LAST_NAME,
      email: MEMBER_USER_EMAIL,
      role: "ADMIN",
      is_alert_action_required_email_enabled: true,
      is_alert_resolved_email_enabled: true,
      is_alert_dismissed_email_enabled: true,
      is_evidence_requested_email_enabled: true,
    },
  });
  createdIds.membership_id = membership.id;
  printJson("Response", membership);
  console.log(`Saved membership_id: ${createdIds.membership_id}`);

  logStep(3, "Create merchant");
  const merchant = await apiRequest({
    method: "POST",
    path: "/v1/merchants/",
    body: {
      organisation_id: createdIds.organisation_id,
      name: MERCHANT_NAME,
      type: MERCHANT_TYPE,
      external_id: MERCHANT_EXTERNAL_ID,
    },
  });
  createdIds.merchant_id = merchant.id;
  printJson("Response", merchant);
  console.log(`Saved merchant_id: ${createdIds.merchant_id}`);

  logStep(4, "Create custom orders integration");
  const integration = await apiRequest({
    method: "POST",
    path: "/v1/integrations/",
    body: {
      organisation_id: createdIds.organisation_id,
      name: INTEGRATION_NAME,
      type: "CUSTOM_ORDERS",
      status: "ENABLED",
      merchant_ids: [createdIds.merchant_id],
    },
  });
  createdIds.integration_id = integration.id;
  printJson("Response", integration);
  console.log(`Saved integration_id: ${createdIds.integration_id}`);

  logStep(5, "Create Ethoca enrolment");
  const ethocaEnrolment = await apiRequest({
    method: "POST",
    path: "/v2/enrolments/",
    body: {
      merchant_ids: [createdIds.merchant_id],
      type: "ETHOCA_ALERT",
      ethoca_alert: {
        descriptors: [
          {
            descriptor: "ACMEFIT",
            match_type: "STARTS_WITH",
          },
        ],
      },
    },
  });
  createdIds.ethoca_enrolment_id = ethocaEnrolment.id;
  printJson("Response", ethocaEnrolment);
  console.log(`Saved ethoca_enrolment_id: ${createdIds.ethoca_enrolment_id}`);

  logStep(6, "Create Verifi RDR enrolment");
  const verifiEnrolment = await apiRequest({
    method: "POST",
    path: "/v2/enrolments/",
    body: {
      merchant_ids: [createdIds.merchant_id],
      type: "VERIFI_RDR",
      verifi_rdr: {
        bin: "424242",
        caid: "ABC1234567",
      },
    },
  });
  createdIds.verifi_enrolment_id = verifiEnrolment.id;
  printJson("Response", verifiEnrolment);
  console.log(`Saved verifi_enrolment_id: ${createdIds.verifi_enrolment_id}`);

  logStep(7, "Submit COMPLETE order for alert matching");
  const orderResponse = await apiRequest({
    method: "POST",
    path: "/v1/orders/",
    body: [
      {
        type: "COMPLETE",
        organisation_id: createdIds.organisation_id,
        integration_id: createdIds.integration_id,
        reference_id: ORDER_REFERENCE_ID,
        order_datetime: "2026-02-17T09:59:00Z",
        order_number: ORDER_NUMBER,
        order_subtotal_amount_in_cents: 4900,
        order_currency: "USD",
        order_total_amount_in_cents: 4900,
        order_status: "CLOSED_COMPLETE",
        customer_email: ORDER_CUSTOMER_EMAIL,
        transactions: [
          {
            reference_id: ORDER_TRANSACTION_REFERENCE_ID,
            amount_in_cents: 4900,
            currency: "USD",
            payment_method_type: "CARD",
            authorisation_status: "SETTLED",
            payment_method_reference_id: "pi_3QJ2ABCD12345678",
            authorised_at: "2026-02-17T09:59:05Z",
            payment_method_card_brand: "VISA",
            payment_method_card_last_4: "4242",
            acquirer_reference_number: "74027012345678901234567",
          },
        ],
      },
    ],
  });
  printJson("Response", orderResponse);

  if (orderResponse.failed > 0) {
    console.log("\nOrder was not created. API returned validation errors.");
  } else if (orderResponse.results && orderResponse.results.length > 0) {
    createdIds.order_id = orderResponse.results[0].id;
  }

  console.log("\nDone. IDs to save for future requests:");
  printJson("Created IDs", createdIds);
}

main().catch((error) => {
  fail("Unexpected script error", {
    message: error.message,
    stack: error.stack,
  });
});

Actioning alerts

Every Ethoca alert with ACTION_REQUIRED status is expected to be actioned by the user. The deadline is represented by the action_required_deadline field in the alert object. However, we recommend sending your chosen action as soon as possible. After actioning, the alert status moves to RESOLVED and can no longer be changed. Verifi RDR alerts arrive already resolved and are not actionable.

Behind the scenes, actioning an alert updates Ethoca with the outcome. This allows them to stop the dispute if the user proactively refunded the transaction, or let the dispute proceed if you chose to fight it.

For actions that refund the transaction, if the merchant account is integrated with a payment provider (e.g. Stripe), we automatically refund the payment. You can also choose to cancel the subscription associated with the payment.

The update alert endpoint (PATCH /v1/alerts/{alert_id}) takes the following parameters:

  • action - the action to perform on the alert (see below)

  • note - an optional customer-facing comment/note about the alert

The action parameter can be one of four values:

  • REFUND

  • CANCEL

  • REFUND_AND_CANCEL

  • ACCEPT_DISPUTE

Let's explore how each of these values works.

REFUND

  • We let the alert provider know that you refunded the alert.

  • If your merchant account is integrated with a payment provider, we automatically refund the payment.

CANCEL

  • We let the alert provider know that you did not refund the alert.

  • If your merchant account is integrated with a payment provider and there is a subscription associated with the alert, we automatically cancel it.

REFUND_AND_CANCEL

  • We let the alert provider know that you refunded the alert.

  • If your merchant account is integrated with a payment provider, we automatically refund the payment.

  • If your merchant account is integrated with a payment provider and there is a subscription associated with the alert, we automatically cancel it.

ACCEPT_DISPUTE

  • We let the alert provider know that you did not refund the alert.

If you have not integrated ChargebackStop with any payment processing services, you will most likely use only REFUND and ACCEPT_DISPUTE.

This API is also available to your customers. They can generate API keys for their own organisations in the organisation dashboard and action alerts via API.

Matching an alert to a transaction

If you enabled payment processor integrations in your organisations and enabled automatic matching, GET /v1/alerts responses include two additional fields:

  • integration_id - ChargebackStop integration ID that contains the transaction matched to this alert

  • integration_transaction_id - transaction ID matched to this alert in your payment processor. For Stripe, this looks like pi_3SPJO4KRFSLReU4y04XJUvLN. For other processors it varies.

Resolution models: Ethoca vs Verifi RDR

Ethoca and Verifi RDR resolve alerts through fundamentally different mechanisms. Both flows are documented in detail in the sections above — use this contrast as a quick reference.

  • Ethoca (ETHOCA_ALERT) — Alerts enter an ACTION_REQUIRED state with a deadline (action_required_deadline). You (or your customer) must call PATCH /v1/alerts/{alert_id} with one of REFUND, CANCEL, REFUND_AND_CANCEL, or ACCEPT_DISPUTE before the deadline. If no action is taken, the dispute proceeds. Resolution rulesets can auto-action matched alerts when a payment processor integration is in place, but the underlying mechanism is still the same PATCH call applied on the customer's behalf.

  • Verifi RDR (VERIFI_RDR) — Alerts are resolved synchronously at the network level. Each incoming alert is evaluated against the enrolment's resolution ruleset and the chosen outcome is applied automatically. There is no ACTION_REQUIRED state, no deadline, and no PATCH /v1/alerts/{alert_id} call to make — the action API is Ethoca-only. With no ruleset configured, every RDR alert is refunded by default (see step 6 of the onboarding flow).

Test organisation simulations

In TEST mode, you can use simulation endpoints to test webhooks and actioning alerts.

Simulations API docs.

Last updated

Was this helpful?