API integration guide

Full API documentation is available here: https://api.chargebackstop.com/v1/docsarrow-up-right.

V2 API documentation (enrolments endpoint only for now): https://api.chargebackstop.com/v2/docsarrow-up-right.

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": "[email protected]",
    "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": "[email protected]",
  "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 v2 API: ../../api/enrolments_v2.md

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

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

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": "[email protected]",
      "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",
      "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.

7

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 = "[email protected]";
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

circle-exclamation

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

Behind the scenes, actioning an alert updates the alert provider (e.g. 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 (/v1/alerts/{alert_id}) takes an action parameter. It 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.

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?