NAV Navbar

chargehound

API
cURL Node Python Ruby Go Java

Overview

Chargehound's API is organized around REST. JSON is returned by all API responses, including errors, although our API libraries convert responses to appropriate language-specific objects. All API URLs listed in this documentation are relative to https://api.chargehound.com/v1/. For example, the /disputes/ resource is located at https://api.chargehound.com/v1/disputes.

All requests must be made over HTTPS.

Authentication

curl -u test_123:
var chargehound = require('chargehound')(
  'test_123'
);
import chargehound
chargehound.api_key = 'test_123'
require 'chargehound'
Chargehound.api_key = 'test_123'
import (
  "github.com/chargehound/chargehound-go"
)

ch := chargehound.New("test_123") 
import com.chargehound.Chargehound;

Chargehound chargehound = new Chargehound("test_123");

Make sure to replace test_123 with your API key.

You have two API keys, one for test and one for live data.

Authentication to the API is performed via HTTP Basic Auth. Your API key serves as your username. You do not need to provide a password, so the full authorization string should have the form {{key}}:.

If you are setting authentication in the HTTP Headers with the form Authorization: Basic {{credentials}} be sure to base 64 encode the credentials so that, for example, test_123: becomes dGVzdF9YWFg6.

Errors

{
  "url": "/v1/disputes/puppy/submit",
  "livemode": false,
  "error": {
    "status": 404,
    "message": "A dispute with id 'puppy' was not found"
  }
}

Chargehound uses conventional HTTP response codes to indicate success or failure of an API request. In general, codes in the 2xx range indicate success, codes in the 4xx range indicate an error that resulted from the provided information (e.g. a required parameter was missing, a payment failed, etc.), and codes in the 5xx range indicate an error with our servers.

HTTP status code summary

Status Description
200 - OK Everything worked as expected.
201 - Created The resource was successfully created.
202 - Accepted The request was successfully processed but not completed.
400 - Bad Request The request data was invalid or incomplete.
401 - Unauthorized Invalid API key provided.
403 - Forbidden Insecure connection.
404 - Not Found Resource could note be found.
500 - Server Error Something went wrong on Chargehound's end.

Handling errors

var Chargehound = require('chargehound')('test_123')

// Use the Chargehound library to make a request
.then(function () {
  // handle success
})
.catch(function (err) {
  if (err instanceof Chargehound.error.ChargehoundBadRequestError) {
    // Invalid parameters were supplied in the request

    console.log('Status is: ' + err.status)
    console.log('Message is: ' + err.message)

  } else if (err instanceof Chargehound.error.ChargehoundAuthenticationError) {
    // Incorrect or missing API key
  } else if (err instanceof Chargehound.error.ChargehoundError) {
    // Generic Chargehound error (404, 500, etc.)
  } else {
    // Handle any other types of unexpected errors
  }
})
from chargehound.error import (
  ChargehoundError, ChargehoundBadRequestError, 
  ChargehoundAuthenticationError
)

try:
  # Use the Chargehound library to make a request
except ChargehoundBadRequestError, e:
  # Invalid parameters were supplied in the request

  print 'Status is: %s' % e.status
  print 'Message is: %s' % e.message

  pass
except ChargehoundAuthenticationError, e:
  # Incorrect or missing API key
  pass
except ChargehoundError, e:
  # Generic Chargehound error (404, 500, etc.)
  pass
except Exception, e:
  # Handle any other types of unexpected errors
  pass
require 'chargehound/error'

begin
  # Use the Chargehound library to make a request
rescue Chargehound::ChargehoundBadRequestError => e
  # Invalid parameters were supplied in the request

  puts 'Status is: #{e.status}'
  puts 'Message is: #{e.message}'

rescue Chargehound::ChargehoundAuthenticationError => e
  # Incorrect or missing API key
rescue Chargehound::ChargehoundTimeoutError => e
  # The request timed out (default timeout is 60 seconds)
rescue Chargehound::ChargehoundError => e
  # Generic Chargehound error (404, 500, etc.)
rescue => e
  # Handle any other types of unexpected errors
end
import (
  "fmt"
  "github.com/chargehound/chargehound-go"
)

// Use the Chargehound library to make a request

chErr := err.(chargehound.Error)

switch chErr.Type() {
case BadRequestError:
  // Invalid parameters were supplied in the request

  fmt.Println(chErr.Error())

case chargehound.UnauthorizedError:
  // Missing API key
case chargehound.ForbiddenError:
  // Incorrect API key
case chargehound.NotFoundError:
  // Not found
case chargehound.InternalServerError:
  // Internal server error
case chargehound.GenericError:
  // Generic Chargehound error 
default:
  // Handle any other types of unexpected errors
}
import com.chargehound.errors.ChargehoundException;

try {
  // Use the Chargehound library to make a request
} catch (ChargehoundException.HttpException.Unauthorized exception) {
  // Missing API key
} catch (ChargehoundException.HttpException.Forbidden exception) {
  // Incorrect API key
} catch (ChargehoundException.HttpException.NotFound exception) {
  // Not found
} catch (ChargehoundException.HttpException.BadRequest exception) {
  // Bad request
} catch (ChargehoundException.HttpException.InternalServerError exception) {
  // Internal server error
} catch (ChargehoundException exception) {
  // Generic Chargehound error
} catch (Exception exception) {
  // Handle any other types of unexpected errors
}

When using our client libraries Chargehound also provides typed exceptions when errors are returned from the API.

Libraries

Chargehound offers wrapper libraries in the following languages:

HTTP

When sending a body along with Content-Type: application/json, the Chargehound API expects JSON.

curl -X PUT https://api.chargehound.com/v1/disputes/dp_123 \
  -u test_123: \
  -H "Content-Type: application/json" \
  -d "{\"fields\": { \"product_url\":  \"http://www.example.com/products/cool\" } }"

When sending a body along with Content-Type: application/x-www-form-urlencoded, the Chargehound API expects form data. This Content Type is set automatically by curl. Dictionaries can be expressed with square brackets.

curl -X PUT https://api.chargehound.com/v1/disputes/dp_123 \
  -u test_123: \
  -d fields[product_url]=http://www.example.com/products/cool

If you are making HTTP requests directly, be sure to set the Content-Type header in PUT/POST requests to specify the format of the body. The Chargehound API supports JSON and URL encoding.

Versioning

The Chargehound API uses versioning to roll out backwards-incompatible changes over time.

About Versioning

The API version will control the API and webhook behaviors, such as parameters accepted in requests, and response properties. Your account was automatically set to the latest version when you signed up.

A new version of the API is released when backwards-incompatible changes are made to the API. To avoid breaking your code, we will never force you to upgrade until you’re ready.

We will be releasing backwards-compatible changes without introducing new versions. Your code will be able to handle these changes no matter what version it's on.

Examples of backwards-incompatible changes:

Examples of backwards-compatible changes:

Upgrading API Version

We recommend staying up-to-date with the current API version to take advantage of latest improvements to the Chargehound API.

To see your current version and upgrade to the latest, visit the API Version section of the API tab on the Chargehound dashboard here.

All requests to the API will use your organization API settings, unless you override the API version. Versioning of the Chargehound API will be released as dates, displayed as: YYYY-MM-DD

The API version used by your webhooks can be configured individually for each webhook URL you have configured in the Webhook URLs section of the API tab on the Chargehound dashboard here.

Testing API Version

To set the API version on a specific request, send a Chargehound-Version header. The API version will be set to the version that you specify for that individual request.

curl -X POST https://api.chargehound.com/v1/disputes \
  -u test_123: \
  -H "Chargehound-Version: YYYY-MM-DD"
var chargehound = require('chargehound')('test_123', {
  version: 'YYYY-MM-DD'
});
import chargehound
chargehound.api_key = 'test_123'
chargehound.version = 'YYYY-MM-DD'
require 'chargehound'
Chargehound.api_key = 'test_123'
Chargehound.version = 'YYYY-MM-DD'
import (
  "github.com/chargehound/chargehound-go"
)

ch := chargehound.New("test_123",
  &chargehound.ClientParams{APIVersion: "YYYY-MM-DD"})
import com.chargehound.Chargehound;
Chargehound chargehound = new Chargehound("test_123");
chargehound.setApiVersion("YYYY-MM-DD");

Changelog

Version 2017-10-30

In this API version, we’ve cleaned up some attribute names in order to make them more consistent and intuitive.

Documentation

Documentation is available for all releases:

Disputes

The dispute object

Dispute objects represent a dispute created on a charge. They can also be referred to as chargebacks. In order to contest a dispute, attach a template and update the dispute with the template's required fields.

A dispute object is:

Field Type Description
id string A unique identifier for the dispute. This id is set by the payment processor of the dispute.
state string State of the dispute. One of needs_response,submitted, under_review, won, lost, warning_needs_response, warning_under_review, warning_closed , response_disabled, charge_refunded, requires_review, accepted, queued.
reason string Reason for the dispute. One of general, fraudulent, duplicate, subscription_canceled, product_unacceptable, product_not_received, unrecognized, credit_not_processed, incorrect_account_details, insufficient_funds, bank_cannot_process, debit_not_authorized, goods_services_returned_or_refused, goods_services_cancelled, transaction_amount_differs, retrieved, customer_initiated
charged_at string ISO 8601 timestamp - when the charge was made.
disputed_at string ISO 8601 timestamp - when the charge was disputed.
due_by string ISO 8601 timestamp - when dispute evidence needs to be disputed by.
submitted_at string ISO 8601 timestamp - when dispute evidence was submitted.
closed_at string ISO 8601 timestamp - when the dispute was resolved.
submitted_count integer Number of times the dispute evidence has been submitted.
template string Id of the template attached to the dispute.
fields dictionary Evidence fields attached to the dispute.
missing_fields dictionary Any fields required by the template that have not yet been provided.
products array A list of products in the disputed order. (See Product data for details.)
charge string Id of the disputed charge. This id is set by the payment processor of the dispute.
is_charge_refundable boolean Can the charge be refunded.
amount integer Amount of the disputed charge. Amounts are in cents (or other minor currency unit.)
currency string Currency code of the disputed charge. e.g. 'USD'.
fee integer The amount deducted due to the payment processor's chargeback fee. Amounts are in cents (or other minor currency unit.)
reversal_amount integer The amount deducted due to the chargeback. Amounts are in cents (or other minor currency unit.)
reversal_currency string Currency code of the deduction amount. e.g. 'USD'.
customer string Id of the customer (if any). This id is set by the payment processor of the dispute.
customer_name string Name of the customer (if any).
customer_email string Email of the customer (if any).
customer_purchase_ip string IP of purchase (if available).
address_zip string Billing address zip of the charge.
address_line1_check string State of address check (if available). One of pass, fail, unavailable, checked.
address_zip_check string State of address zip check (if available). One of pass, fail, unavailable, checked.
cvc_check string State of cvc check (if available). One of pass, fail, unavailable, checked.
statement_descriptor string The descriptor that appears on the customer's credit card statement for this change.
account_id string The account id for accounts that are charged directly through Stripe (if any). (See Stripe charging directly for details.)
created string ISO 8601 timestamp - when the dispute was created in Chargehound.
updated string ISO 8601 timestamp - when the dispute was last updated in Chargehound.
source string The source of the dispute. One of mock, api, braintree, vantiv, adyen, worldpay or stripe
processor string The payment processor of the dispute. One of braintree, vantiv, adyen, worldpay or stripe
kind string The kind of the dispute. One of chargeback, pre_arbitration or retrieval
account string The Id of the connected account for this dispute.
reference_url string Custom URL with dispute information, such as the dispute or charge in your company dashboard.

Submitting a dispute

Definition:

POST /v1/disputes/{{dispute_id}}/submit
chargehound.Disputes.submit();
chargehound.Disputes.submit()
Chargehound::Disputes.submit
ch.Disputes.Submit(*chargehound.UpdateDisputeParams)
chargehound.disputes.submit();

Example request:

curl -X POST https://api.chargehound.com/v1/disputes/dp_123/submit \
  -u test_123: \
  -d template=unrecognized \
  -d fields[customer_name]="Susie Chargeback" 
var chargehound = require('chargehound')(
  'test_123'
);

chargehound.Disputes.submit('dp_123', {
  template: 'unrecognized',
  fields: {
    customer_name: 'Susie Chargeback'
  }
}, function (err, res) {
  // ...
});
import chargehound
chargehound.api_key = 'test_123'

chargehound.Disputes.submit('dp_123',
  template='unrecognized',
  fields={
    'customer_name': 'Susie Chargeback'
  }
)
require 'chargehound'
Chargehound.api_key = 'test_123'

Chargehound::Disputes.submit('dp_123',
  template: 'unrecognized',
  fields: {
    'customer_name' => 'Susie Chargeback'
  }
)
import (
  "github.com/chargehound/chargehound-go"
)

ch := chargehound.New("test_123", nil)

params := chargehound.UpdateDisputeParams{
  ID:       "dp_123",
  Template: "unrecognized",
  Fields: map[string]interface{}{
    "customer_name": "Susie Chargeback",
  },
}

dispute, err := ch.Disputes.Submit(&params)
import com.chargehound.Chargehound;
import com.chargehound.models.Dispute;

Chargehound chargehound = new Chargehound("test_123");

Map<String, Object> fields = new HashMap<String, Object>();
fields.put("customer_name", "Susie Chargeback");

chargehound.disputes.submit("dp_123",
  new Dispute.UpdateParams.Builder()
    .template("unrecognized")
    .fields(fields)
    .finish()
);

Example response:

{
  "customer": "cus_123",
  "livemode": false,
  "updated": "2016-10-18T20:38:51",
  "currency": "usd",
  "missing_fields": {},
  "address_zip_check": "pass",
  "closed_at": null,
  "id": "dp_123",
  "customer_name": "Susie Chargeback",
  "fee": 1500,
  "reversal_amount": 500,
  "due_by": "2016-11-18T20:38:51",
  "state": "submitted",
  "statement_descriptor": "COMPANY",
  "source": "stripe",
  "charge": "ch_123",
  "template": "unrecognized",
  "is_charge_refundable": false,
  "cvc_check": "unavailable",
  "customer_email": "susie@example.com",
  "account_id": null,
  "address_line1_check": "pass",
  "object": "dispute",
  "customer_purchase_ip": null,
  "disputed_at": "2016-09-18T20:38:51",
  "submitted_count": 0,
  "reason": "unrecognized",
  "reversal_total": 2000,
  "reversal_currency": "usd",
  "address_zip": null,
  "submitted_at": "2016-10-18T20:38:51",
  "created": "2016-09-18T20:38:51",
  "url": "/v1/disputes/dp_123",
  "fields": {
    "customer_name": "Susie Chargeback"
  },
  "charged_at": "2016-09-18T20:38:51",
  "products": [],
  "reference_url": null,
  "amount": 500,
  "processor": "stripe",
  "account": "default"
}

You will want to submit the dispute through Chargehound after you receive the dispute.created webhook notification. With one POST request you can update a dispute with the evidence fields and send the generated response to the source payment processor.

The dispute will be in the submitted state if the submit was successful.

Parameters

Parameter Type Required? Description
template string optional The id of the template to use.
fields dictionary optional Key value pairs to hydrate the template's evidence fields.
products array optional List of products the customer purchased. (See Product data for details.)
reference_url string optional Custom URL with dispute information, such as the dispute or charge in your company dashboard.
queue boolean optional Queue the dispute for submission. (See Queuing for submission for details.)
force boolean optional Skip the manual review filters or submit a dispute in manual review. (See Manual review for details.)
account string optional Id of the connected account for this dispute (if multiple accounts are connected). View your connected accounts in the Chargehound dashboard settings page here.

Possible errors

Error code Description
400 Bad Request Dispute has no template, or missing fields required by the template.

Creating a dispute

Disputes are usually not created via the REST API. Instead, once your payment processor is connected we will mirror disputes via webhooks. You will reference the dispute with the same id that is used by the payment processor. If you are working on a standalone integration, please refer to this section.

Retrieving a list of disputes

Definition:

GET /v1/disputes
chargehound.Disputes.list();
chargehound.Disputes.list()
Chargehound::Disputes.list
ch.Disputes.List(*chargehound.ListDisputesParams)
chargehound.disputes.list();

Example request:

curl https://api.chargehound.com/v1/disputes \
  -u test_123:
var chargehound = require('chargehound')(
  'test_123'
);

chargehound.Disputes.list(null, function (err, res) {
  // ...
});
import chargehound
chargehound.api_key = 'test_123'

chargehound.Disputes.list()
require 'chargehound'
Chargehound.api_key = 'test_123'

Chargehound::Disputes.list
import (
  "github.com/chargehound/chargehound-go"
)

ch := chargehound.New("test_123", nil)

disputeList, err := ch.Disputes.List(nil)
import com.chargehound.Chargehound;

Chargehound chargehound = new Chargehound("test_123");

chargehound.disputes.list();

Example response:

{
  "has_more": false,
  "url": "/v1/disputes",
  "livemode": false,
  "object": "list",
  "data": [
    {
      "customer": "cus_123",
      "updated": null,
      "currency": "usd",
      "missing_fields": {},
      "address_zip_check": "pass",
      "closed_at": null,
      "id": "dp_123",
      "customer_name": "Susie Chargeback",
      "fee": 1500,
      "reversal_amount": 500,
      "due_by": "2016-11-18T20:38:51",
      "state": "needs_response",
      "statement_descriptor": "COMPANY",
      "source": "stripe",
      "charge": "ch_123",
      "template": null,
      "is_charge_refundable": false,
      "cvc_check": "unavailable",
      "customer_email": "susie@example.com",
      "account_id": null,
      "address_line1_check": "pass",
      "object": "dispute",
      "customer_purchase_ip": null,
      "disputed_at": "2016-09-18T20:38:51",
      "submitted_count": 0,
      "reason": "unrecognized",
      "reversal_total": 2000,
      "reversal_currency": "usd",
      "address_zip": null,
      "submitted_at": null,
      "created": "2016-09-18T20:38:51",
      "url": "/v1/disputes/dp_123",
      "fields": {},
      "charged_at": "2016-09-18T20:38:51",
      "products": [],
      "reference_url": null,
      "amount": 500,
      "processor": "stripe",
      "account": "default"
    }
  ]
}

This endpoint will list all the disputes that we have synced from your payment processor(s). By default the disputes will be ordered by created with the most recent dispute first. has_more will be true if more results are available.

Parameters

Parameter Type Required? Description
limit integer optional Maximum number of disputes to return. Default is 20, maximum is 100.
starting_after string optional A dispute id. Fetch the next page of disputes (disputes created before this dispute).
ending_before string optional A dispute id. Fetch the previous page of disputes (disputes created after this dispute).
state string optional Dispute state. Will only fetch disputes with the state.
account string optional Account id. Will only fetch disputes under that connected account. View your connected accounts in the Chargehound dashboard settings page here.

Retrieving a dispute

Definition:

GET /v1/disputes/{{dispute_id}}
chargehound.Disputes.retrieve();
chargehound.Disputes.retrieve()
Chargehound::Disputes.retrieve
ch.Disputes.Retrieve(*chargehound.RetrieveDisputeParams)
chargehound.disputes.retrieve();

Example request:

curl https://api.chargehound.com/v1/disputes/dp_123 \
  -u test_123:
var chargehound = require('chargehound')(
  'test_123'
);

chargehound.Disputes.retrieve('dp_123', function (err, res) {
  // ...
});
import chargehound
chargehound.api_key = 'test_123'

chargehound.Disputes.retrieve('dp_123')
require 'chargehound'
Chargehound.api_key = 'test_123'

Chargehound::Disputes.retrieve('dp_123')
import (
  "github.com/chargehound/chargehound-go"
)

ch := chargehound.New("test_123", nil)

params := chargehound.RetrieveDisputeParams{
  ID: "dp_123",
}

dispute, err := ch.Disputes.Retrieve(&params)
import com.chargehound.Chargehound;

Chargehound chargehound = new Chargehound("test_123");

chargehound.disputes.retrieve("dp_123");

Example response:

{
  "customer": "cus_123",
  "livemode": false,
  "updated": null,
  "currency": "usd",
  "missing_fields": {},
  "address_zip_check": "pass",
  "closed_at": null,
  "id": "dp_123",
  "customer_name": "Susie Chargeback",
  "fee": 1500,
  "reversal_amount": 500,
  "due_by": "2016-11-18T20:38:51",
  "state": "needs_response",
  "statement_descriptor": "COMPANY",
  "source": "stripe",
  "charge": "ch_123",
  "template": null,
  "is_charge_refundable": false,
  "cvc_check": "unavailable",
  "customer_email": "susie@example.com",
  "account_id": null,
  "address_line1_check": "pass",
  "object": "dispute",
  "customer_purchase_ip": null,
  "disputed_at": "2016-09-18T20:38:51",
  "submitted_count": 0,
  "reason": "unrecognized",
  "reversal_total": 2000,
  "reversal_currency": "usd",
  "address_zip": null,
  "submitted_at": null,
  "created": "2016-09-18T20:38:51",
  "url": "/v1/disputes/dp_123",
  "fields": {},
  "charged_at": "2016-09-18T20:38:51",
  "products": [],
  "reference_url": null,
  "amount": 500,
  "processor": "stripe",
  "account": "default"
}

You can retrieve a single dispute by its id.

Updating a dispute

Definition:

PUT /v1/disputes/{{dispute_id}}
chargehound.Disputes.update();
chargehound.Disputes.update()
Chargehound::Disputes.update
ch.Disputes.Update(*chargehound.UpdateDisputeParams)
chargehound.disputes.update();

Example request:

curl -X PUT https://api.chargehound.com/v1/disputes/dp_123 \
  -u test_123: \
  -d template=unrecognized \
  -d fields[customer_name]="Susie Chargeback" 
var chargehound = require('chargehound')(
  'test_123'
);

chargehound.Disputes.update('dp_123', {
  template: 'unrecognized',
  fields: {
    customer_name: 'Susie Chargeback'
  }
}, function (err, res) {
  // ...
});
import chargehound
chargehound.api_key = 'test_123'

chargehound.Disputes.update('dp_123',
  template='unrecognized',
  fields={
    'customer_name': 'Susie Chargeback'
  }
)
require 'chargehound'
Chargehound.api_key = 'test_123'

Chargehound::Disputes.update('dp_123',
  template: 'unrecognized',
  fields: {
    'customer_name' => 'Susie Chargeback'
  }
)
import (
  "github.com/chargehound/chargehound-go"
)

ch := chargehound.New("test_123", nil)

params := chargehound.UpdateDisputeParams{
  ID:       "dp_123",
  Template: "unrecognized",
  Fields: map[string]interface{}{
    "customer_name": "Susie Chargeback",
  },
}

dispute, err := ch.Disputes.Update(&params)
import com.chargehound.Chargehound;
import com.chargehound.models.Dispute;

Chargehound chargehound = new Chargehound("test_123");

Map<String, Object> fields = new HashMap<String, Object>();
fields.put("customer_name", "Susie Chargeback");

chargehound.disputes.update("dp_123",
  new Dispute.UpdateParams.Builder()
    .template("unrecognized")
    .fields(fields)
    .finish()
);

Example response:

{
  "customer": "cus_123",
  "livemode": false,
  "updated": "2016-10-18T20:38:51",
  "currency": "usd",
  "missing_fields": {},
  "address_zip_check": "pass",
  "closed_at": null,
  "id": "dp_123",
  "customer_name": "Susie Chargeback",
  "fee": 1500,
  "reversal_amount": 500,
  "due_by": "2016-11-18T20:38:51",
  "state": "needs_response",
  "statement_descriptor": "COMPANY",
  "source": "stripe",
  "charge": "ch_123",
  "template": "unrecognized",
  "is_charge_refundable": false,
  "cvc_check": "unavailable",
  "customer_email": "susie@example.com",
  "account_id": null,
  "address_line1_check": "pass",
  "object": "dispute",
  "customer_purchase_ip": null,
  "disputed_at": "2016-09-18T20:38:51",
  "submitted_count": 0,
  "reason": "unrecognized",
  "reversal_total": 2000,
  "reversal_currency": "usd",
  "address_zip": null,
  "submitted_at": null,
  "created": "2016-09-18T20:38:51",
  "url": "/v1/disputes/dp_123",
  "fields": {
    "customer_name": "Susie Chargeback"
  },
  "charged_at": "2016-09-18T20:38:51",
  "products": [],
  "reference_url": null,
  "amount": 500,
  "processor": "stripe",
  "account": "default"
}

You can update the template and the fields on a dispute.

Parameters

Parameter Type Required? Description
template string optional The id of the template to use.
fields dictionary optional Key value pairs to hydrate the template's evidence fields.
products array optional List of products the customer purchased. (See Product data for details.)
reference_url string optional Custom URL with dispute information, such as the dispute or charge in your company dashboard.
submit boolean optional Submit dispute evidence immediately after update. If the submit fails, updated fields will still be saved.
queue boolean optional Queue the dispute for submission. (See Queuing for submission for details.)
force boolean optional Skip the manual review filters or submit a dispute in manual review. (See Manual review for details.)

Possible errors

Error code Description
400 Bad Request Dispute has no template, or missing fields required by the template.

Queuing for submission

Queuing a dispute for submission allows you to stage evidence that will be automatically submitted at a later time. Typically a payment processor only allows a dispute response to be submitted once, making it impossible to edit the response. Queuing a dispute for submission allows you to make changes to the dispute's response while being confident that the dispute will be submitted on time.

You can queue a dispute by setting the queue parameter to true when making a request to submit or create a dispute. The dispute will be in the queued state if the request was successful.

Accepting a dispute

Definition:

POST /v1/disputes/{{dispute_id}}/accept
chargehound.Disputes.accept();
chargehound.Disputes.accept()
Chargehound::Disputes.accept
ch.Disputes.Accept(*chargehound.AcceptDisputeParams)
chargehound.disputes.accept();

Example request:

curl -X POST https://api.chargehound.com/v1/disputes/dp_123/accept \
  -u test_123:
var chargehound = require('chargehound')(
  'test_123'
);

chargehound.Disputes.accept('dp_123', function (err, res) {
  // ...
});
import chargehound
chargehound.api_key = 'test_123'

chargehound.Disputes.accept('dp_123')
require 'chargehound'
Chargehound.api_key = 'test_123'

Chargehound::Disputes.accept('dp_123')
import (
  "github.com/chargehound/chargehound-go"
)

ch := chargehound.New("test_123", nil)

params := chargehound.AcceptDisputeParams{
  ID:       "dp_123"
}

dispute, err := ch.Disputes.Accept(&params)
import com.chargehound.Chargehound;

Chargehound chargehound = new Chargehound("test_123");

chargehound.disputes.accept("dp_123");

Example response:

{
  "customer": "cus_123",
  "livemode": false,
  "updated": "2016-10-18T20:38:51",
  "currency": "usd",
  "missing_fields": {},
  "address_zip_check": "pass",
  "closed_at": null,
  "id": "dp_123",
  "customer_name": "Susie Chargeback",
  "fee": 1500,
  "reversal_amount": 500,
  "due_by": "2016-11-18T20:38:51",
  "state": "accepted",
  "statement_descriptor": "COMPANY",
  "source": "stripe",
  "charge": "ch_123",
  "template": "unrecognized",
  "is_charge_refundable": false,
  "cvc_check": "unavailable",
  "customer_email": "susie@example.com",
  "account_id": null,
  "address_line1_check": "pass",
  "object": "dispute",
  "customer_purchase_ip": null,
  "disputed_at": "2016-09-18T20:38:51",
  "submitted_count": 0,
  "reason": "unrecognized",
  "reversal_total": 2000,
  "reversal_currency": "usd",
  "address_zip": null,
  "submitted_at": "2016-10-18T20:38:51",
  "created": "2016-09-18T20:38:51",
  "url": "/v1/disputes/dp_123",
  "fields": {},
  "charged_at": "2016-09-18T20:38:51",
  "products": [],
  "reference_url": null,
  "amount": 500,
  "processor": "stripe",
  "account": "default"
}

If you do not wish to respond to a dispute you can accept the dispute. Accepting a dispute will remove the dispute from your queue of disputes that need response. This is intented to help you organize your disputes.

The dispute will be in the accepted state if the request was successful.

Product data

If a customer purchased multiple products in a disputed order, those products can be individually attached to a dispute when updating or submitting the dispute. Each product has the following properties:

Example usage:

curl -X PUT https://api.chargehound.com/v1/disputes/dp_123 \
  -u test_123: \
  -d products="[{
                   \"name\" : \"Saxophone\",
                   \"description\" : \"Alto saxophone, with carrying case\",
                   \"image\" : \"http://s3.amazonaws.com/chargehound/saxophone.png\",
                   \"sku\" : \"17283001272\",
                   \"quantity\" : 1,
                   \"amount\" : 20000,
                   \"url\" : \"http://www.example.com\"
                },{
                   \"name\" : \"Milk\",
                   \"description\" : \"Semi-skimmed Organic\",
                   \"image\" : \"http://s3.amazonaws.com/chargehound/milk.png\",
                   \"sku\" : \"26377382910\",
                   \"quantity\" : \"64oz\",
                   \"amount\" : 400,
                   \"url\" : \"http://www.example.com\"
                }]"
var chargehound = require('chargehound')(
  'test_123'
);

chargehound.Disputes.update('dp_123', {
  products: [{
    'name': 'Saxophone',
    'description': 'Alto saxophone, with carrying case',
    'image': 'http://s3.amazonaws.com/chargehound/saxophone.png',
    'sku': '17283001272',
    'quantity': 1,
    'amount': 20000,
    'url': 'http://www.example.com'
  },{
    'name': 'Milk',
    'description': 'Semi-skimmed Organic',
    'image': 'http://s3.amazonaws.com/chargehound/milk.png',
    'sku': '26377382910',
    'quantity': '64oz',
    'amount': 400,
    'url': 'http://www.example.com'
  }]
}, function (err, res) {
  // ...
});
import chargehound
chargehound.api_key = 'test_123'

chargehound.Disputes.update('dp_123',
  products=[{
     'name': 'Saxophone',
     'description': 'Alto saxophone, with carrying case',
     'image': 'http://s3.amazonaws.com/chargehound/saxophone.png',
     'sku': '17283001272',
     'quantity': 1,
     'amount': 20000,
     'url': 'http://www.example.com'
  }, {
     'name': 'Milk',
     'description': 'Semi-skimmed Organic',
     'image': 'http://s3.amazonaws.com/chargehound/milk.png',
     'sku': '26377382910',
     'quantity': '64oz',
     'amount': 400,
     'url': 'http://www.example.com'
  }]
)
require 'chargehound'
Chargehound.api_key = 'test_123'

Chargehound::Disputes.update('dp_123',
  products: [{
     'name' => 'Saxophone',
     'description' => 'Alto saxophone, with carrying case',
     'image' => 'http =>//s3.amazonaws.com/chargehound/saxophone.png',
     'sku' => '17283001272',
     'quantity' => 1,
     'amount' => 20000,
     'url' => 'http =>//www.example.com'
  },{
     'name' => 'Milk',
     'description' => 'Semi-skimmed Organic',
     'image' => 'http =>//s3.amazonaws.com/chargehound/milk.png',
     'sku' => '26377382910',
     'quantity' => '64oz',
     'amount' => 400,
     'url' => 'http =>//www.example.com'
  }]
)
import (
  "github.com/chargehound/chargehound-go"
)

ch := chargehound.New("test_123", nil)

params := chargehound.UpdateDisputeParams{
  ID:       "dp_2284d5ac6eba4e4e8e9a80df0f9c2287",
  Products: []chargehound.Product{
    {
      Name:        "Saxophone",
      Description: "Alto saxophone, with carrying case",
      Image:       "http://s3.amazonaws.com/chargehound/saxophone.png",
      Sku:         "17283001272",
      Quantity:    1,
      Amount:      20000,
      Url:         "http://www.example.com",
    },
    {
      Name:        "Milk",
      Description: "Semi-skimmed Organic",
      Image:       "http://s3.amazonaws.com/chargehound/milk.png",
      Sku:         "26377382910",
      Quantity:    "64oz",
      Amount:      400,
      Url:         "http://www.example.com",
    },
  },
}

dispute, err := ch.Disputes.Update(&params)
import com.chargehound.Chargehound;
import com.chargehound.models.Dispute;
import com.chargehound.models.Product;

Chargehound chargehound = new Chargehound("test_123");

Product saxophoneProduct = new Product.Builder()
  .name("Saxophone")
  .description("Alto saxophone, with carrying case")
  .image("http://s3.amazonaws.com/chargehound/saxophone.png")
  .sku("17283001272")
  .quantity(1)
  .amount(20000)
  .url("http://www.example.com")
  .finish();

Product milkProduct = new Product.Builder()
  .name("Milk")
  .description("Semi-skimmed Organic")
  .image("http://s3.amazonaws.com/chargehound/milk.png")
  .sku("26377382910")
  .quantity("64oz")
  .amount(400)
  .url("http://www.example.com")
  .finish();

List<Product> products = new ArrayList<Product>();
products.add(saxophoneProduct);
products.add(milkProduct);

chargehound.disputes.submit("dp_123",
  new Dispute.UpdateParams.Builder()
  .products(products)
  .finish()
);

Product data fields

Field Type Required? Description
name string required The name of the product ordered.
quantity string or integer required The number or quantity of this product (e.g. 10 or "64oz").
amount integer required The price paid for this item, in cents (or other minor currency unit).
description string optional A product description - for example, the size or color.
image url optional A URL showing the product image.
sku string optional The stock-keeping unit.
url url optional The URL of the purchased item, if it is listed online.

Manual review

Example usage:

curl -X POST https://api.chargehound.com/v1/disputes/dp_123/submit \
  -u test_123: \
  -d force=true
var chargehound = require('chargehound')(
  'test_123'
);

chargehound.Disputes.submit('dp_123', {
  force: true
}, function (err, res) {
  // ...
});
import chargehound
chargehound.api_key = 'test_123'

chargehound.Disputes.submit('dp_123',
  force=True
)
require 'chargehound'
Chargehound.api_key = 'test_123'

Chargehound::Disputes.submit('dp_123',
  force: true
)
import (
  "github.com/chargehound/chargehound-go"
)

ch := chargehound.New("test_123", nil)

params := chargehound.UpdateDisputeParams{
  Force: true
}

dispute, err := ch.Disputes.Submit(&params)
import com.chargehound.Chargehound;
import com.chargehound.models.Dispute;

Chargehound chargehound = new Chargehound("test_123");

chargehound.disputes.submit("dp_123",
  new Dispute.UpdateParams.Builder()
    .force(true)
    .finish()
);

You might want to have the chance to look over some disputes before you submit your response to the bank, so we allow you create rules to mark certain disputes for manual review.

In order submit a dispute that has been marked for review via the API, you will need to pass an extra force parameter or the dispute will stay in the manual review queue.

You can tell a dispute has been marked for manual review if when you submit it you receive a 202 status and the state does not change to submitted.

Braintree read only

Example usage:

curl -X POST https://api.chargehound.com/v1/disputes/dp_123/submit \
  -u test_123: \
  -d charge=ch_123
var chargehound = require('chargehound')(
  'test_123'
);

chargehound.Disputes.submit('dp_123', {
  charge: 'ch_123'
}, function (err, res) {
  // ...
});
import chargehound
chargehound.api_key = 'test_123'

chargehound.Disputes.submit('dp_123',
  charge='ch_123'
)
require 'chargehound'
Chargehound.api_key = 'test_123'

Chargehound::Disputes.submit('dp_123',
  charge: 'ch_123'
)
import (
  "github.com/chargehound/chargehound-go"
)

ch := chargehound.New("test_123", nil)

params := chargehound.UpdateDisputeParams{
  Charge: "ch_123"
}

dispute, err := ch.Disputes.Submit(&params)
import com.chargehound.Chargehound;
import com.chargehound.models.Dispute;

Chargehound chargehound = new Chargehound("test_123");

chargehound.disputes.submit("dp_123",
  new Dispute.UpdateParams.Builder()
    .charge("ch_123")
    .finish()
);

If Chargehound does not have access to the Braintree disputes API, you'll need to create a Braintree user with disputes access and add their credentials to your Chargehound account. Login to Braintree and create a Braintree user here with role permissions that include viewing and editing disputes. Add the credentials for that user on your settings page here.

You will also need to attach the Braintree transaction id using the charge parameter when updating or submitting disputes using the Chargehound API.

You can always reconnect your Braintree account from the settings page here to grant Chargehound access to the disputes API, this will make your integration easier.

Stripe charging directly

Example usage:

curl -X POST https://api.chargehound.com/v1/disputes/dp_123/submit \
  -u test_123: \
  -d account_id=acct_123
var chargehound = require('chargehound')(
  'test_123'
);

chargehound.Disputes.submit('dp_123', {
  account_id: 'acct_123'
}, function (err, res) {
  // ...
});
import chargehound
chargehound.api_key = 'test_123'

chargehound.Disputes.submit('dp_123',
  account_id='acct_123'
)
require 'chargehound'
Chargehound.api_key = 'test_123'

Chargehound::Disputes.submit('dp_123',
  account_id: 'acct_123'
)
import (
  "github.com/chargehound/chargehound-go"
)

ch := chargehound.New("test_123", nil)

params := chargehound.UpdateDisputeParams{
  AccountID: "acct_123"
}

dispute, err := ch.Disputes.Submit(&params)
import com.chargehound.Chargehound;
import com.chargehound.models.Dispute;

Chargehound chargehound = new Chargehound("test_123");

chargehound.disputes.submit("dp_123",
  new Dispute.UpdateParams.Builder()
    .accountId("acct_123")
    .finish()
);

In order to work with Stripe Managed or Connected account integrations that charge directly, you will need to attach the Stripe account id to the dispute using the account_id parameter. When you receive a webhook to your Connect webhook endpoint, get the account from the event. The account_id is the Stripe account id that you will need to set.

Integration Guide

This section walks through some of the technical details of completing a Chargehound integration.

Getting started

Before you can use the Chargehound API, you will need your API keys. Your API keys can be found on the settings page in the "View API keys" section.

In order to submit a dispute you will also need to specify the template to use. Templates are referenced by an ID. You can see a list of your organization's templates and their IDs on the templates page.

Collecting evidence

Depending on the template that you want to use, you will need to collect specific evidence in order to submit a dispute. The fields hash on a dispute represents this evidence. Chargehound will fill some of the evidence fields automatically, but it is unlikely that we can get all the evidence we need without your help. Using the API you can identify the missing fields that represent the evidence you will need to collect yourself.

When your organization first connected to your payment processor, Chargehound likely imported your current queue of disputes needing response. In order to figure out what evidence you need to collect we will need to look at one of these real disputes, but don't worry, we aren't going to submit the dispute yet. List your disputes using the list endpoint, and choose one.

Once you have chosen a dispute, choose the template that you want to use and copy its ID. Next, attach the template to the dispute using the update endpoint. In the response body look for the missing_fields hash. The missing_fields hash shows which fields are still needed in order to submit the dispute with the chosen template. Now you can figure out how to collect the required evidence.

If you don't have a real dispute for reference, go to the templates page and view the customized documentation for a template.

Formatting fields

When submitting evidence you will encounter a few different types of fields in the missing_fields hash. Currently Chargehound templates can have text, date, number, amount url, and email fields, and each type is validated differently. Here's a breakdown of what Chargehound expects for each type:

Field Type Validation
text string Multi-line strings are ok, but be sensitive to your template layout.
date string Submitted responses will be reviewed by humans so try to format dates to be human readable and friendly, although no specific format is enforced. This is not the place for Unix timestamps.
number integer A number should be an integer, not a float.
amount integer An amount should be an integer that represents the cents (or other minor currency unit) value. E.g. $1 is 100.
url string A URL should be a fully qualified URL including the scheme (http:// or https://).
email string An email should be a valid email address.

Once you have all your evidence properly formatted, use the submit endpoint to submit a dispute. The submit endpoint adds the template and evidence fields to a dispute just like the update endpoint, and it also submits the evidence to be reviewed. If you get a 400 response code or ChargehoundBadRequestError after a submit or update it is probably because one of the evidence fields is not properly formatted. When you get a 201 response code the dispute was successfully submitted and you are done.

Metadata and custom fields

Chargehound tries to automatically collect standard information from your payment processor when a dispute is created. You can also define a whitelist of custom fields and metadata fields that you would like to automatically collect on the processors tab of your team settings page here. These fields will be automatically copied to the evidence fields of your disputes when they are created in Chargehound.

For example, if you add an "order_id" to your Stripe Charges with metadata fields, you could easily access that ID in the fields of the disputes created in Chargehound. You could use the ID to find more relevant evidence data in your system, and/or use the ID in your templates.

Stripe metadata

If you connected a Stripe account, Chargehound can automatically collect data from your Charge, Customer, or Subscription metadata fields.

Braintree custom fields

If you connected a Braintree account, Chargehound can automaticaly collect data from your Transaction or Customer custom fields. Your Braintree custom fields should be "Store-and-pass-back" fields, and the field name given to Chargehound should be the API name.

Queue settings

When you submit a dispute, you can set the queue flag to true so that the dispute is not submitted immediately. This gives your team time to review the evidence while being assured that every dispute will be addressed. You can configure when queued disputes will be submitted on the workflow tab of your team settings page here. Queued disputes will always be submitted before the due date.

Setting up webhooks

In order to automatically submit responses whenever you get a dispute, you will need to set up a webhook handler and handle the dispute.created webhook notification.

Testing webhooks

You can create a test mode webhook in the Chargehound dashboard on the webhooks & API tab of your team settings page here. The webhook will only send notifications for disputes created in the Chargehound test mode. For testing locally, we recommend using a tool like ultrahook to forward the webhooks to a development machine. Once you have tested, remember to configure a live mode webhook.

Using a job queue

You do not need to immediately POST your evidence to the submit endpoint when you receive a dispute created event. This is a good time to use a job queue if you have one. Simply pass the dispute id and (if you need it) the charge id to the job. The task worker can then query your database for the needed evidence and POST the submit to Chargehound when it's ready.

Testing with generated disputes

It's possible to create disputes with randomly generated data in test mode. You can update and submit these disputes as normal, and you will be able to view the generated response. This is a good way to become familiar with Chargehound's API and dashboard.

You can create a dispute from the Chargehound dashboard when in test mode by clicking the "Create a Test Dispute" button: Chargehound test dashboard or simply visiting the create dispute page.

Testing with Stripe

1) Create a token for a card with the dispute trigger code.

curl https://api.stripe.com/v1/tokens \
  -u {{your_stripe_test_key}}: \
  -d card[number]=4000000000000259 \
  -d card[exp_month]=12 \
  -d card[exp_year]=2020 \
  -d card[cvc]=123
var stripe = require('stripe')(
  '{{your_stripe_test_key}}'
);

stripe.tokens.create({
  card: {
    number: '4000000000000259',
    exp_month: 12,
    exp_year: 2020,
    cvc: '123'
  }
}, function (err, token) {
  // ...
});
import stripe
stripe.api_key = '{{your_stripe_test_key}}'

stripe.Token.create(
  card={
    "number": "4000000000000259",
    "exp_month": 12,
    "exp_year": 2020,
    "cvc": "123"
  },
)
require 'stripe'
Stripe.api_key = '{{your_stripe_test_key}}'

Stripe::Token.create(
  :card => {
    :number => '4000000000000259',
    :exp_month => 4,
    :exp_year => 2020,
    :cvc => '314'
  },
)
stripe.Key = "{{your_stripe_test_key}}"

t, err := token.New(&stripe.TokenParams{
  Card: &stripe.CardParams{
        Number: "4000000000000259",
        Month:  "12",
        Year:   "2020",
        CVC:    "123",
    },
})
Stripe.apiKey = "{{your_stripe_test_key}}";

Map<String, Object> tokenParams = new HashMap<String, Object>();
Map<String, Object> cardParams = new HashMap<String, Object>();
cardParams.put("number", "4000000000000259");
cardParams.put("exp_month", 12);
cardParams.put("exp_year", 2020);
cardParams.put("cvc", "123");
tokenParams.put("card", cardParams);

Token.create(tokenParams);

2) Attach that token to a Stripe customer, for easy reuse later.

curl https://api.stripe.com/v1/customers \
  -u {{your_stripe_test_key}}: \
  -d description="Always disputes charges" \
  -d source={{token_from_step_1}}
var stripe = require('stripe')(
  '{{your_stripe_test_key}}'
);

stripe.customers.create({
  description: 'Always disputes charges',
  source: '{{token_from_step_1}}'
}, function (err, customer) {
  // ...
});
import stripe
stripe.api_key = '{{your_stripe_test_key}}'

stripe.Customer.create(
  description="Always disputes charges",
  source="{{token_from_step_1}}"
)
require 'stripe'
Stripe.api_key = '{{your_stripe_test_key}}'

Stripe::Customer.create(
  :description => 'Always disputes charges',
  :source => '{{token_from_step_1}}'
)
stripe.Key = "{{your_stripe_test_key}}"

customerParams := &stripe.CustomerParams{
  Desc: "Always disputes charges",
}
customerParams.SetSource("{{token_from_step_1}}")
c, err := customer.New(customerParams)
Stripe.apiKey = "{{your_stripe_test_key}}";

Map<String, Object> customerParams = new HashMap<String, Object>();
customerParams.put("description", "Always disputes charges");
customerParams.put("source", "{{token_from_step_1}}");

Customer.create(customerParams);

3) Create a charge that will trigger a dispute. You can view the resulting dispute in the Stripe dashboard.

curl https://api.stripe.com/v1/charges \
  -u {{your_stripe_test_key}}: \
  -d amount=701 \
  -d currency=usd \
  -d customer={{customer_from_step_2}} \
  -d description="Triggering a dispute"
var stripe = require('stripe')(
  '{{your_stripe_test_key}}'
);

stripe.charges.create({
  amount: 400,
  currency: 'usd',
  source: '{{customer_from_step_2}}', // obtained with Stripe.js
  description: 'Charge for test@example.com'
}, function (err, charge) {
  // ...
});
import stripe
stripe.api_key = '{{your_stripe_test_key}}'

stripe.Charge.create(
  amount=400,
  currency="usd",
  customer="{{customer_from_step_2}}",
  description="Triggering a dispute"
)
require 'stripe'
Stripe.api_key = '{{your_stripe_test_key}}'

Stripe::Charge.create(
  :amount => 701,
  :currency => 'usd',
  :customer => '{{customer_from_step_2}}',
  :description => 'Triggering a dispute'
)
stripe.Key = "{{your_stripe_test_key}}"

chargeParams := &stripe.ChargeParams{
  Amount: 400,
  Currency: "usd",
  Desc: "Triggering a dispute",
}
chargeParams.SetSource("{{customer_from_step_2}}")
ch, err := charge.New(chargeParams)
Stripe.apiKey = "{{your_stripe_test_key}}";

Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("amount", 400);
chargeParams.put("currency", "usd");
chargeParams.put("description", "Triggering a dispute");
chargeParams.put("source", "{{customer_from_step_2}}");

Charge.create(chargeParams);

4) Once the dispute is created in Stripe, you will see it mirrored in Chargehound.

curl https://api.chargehound.com/v1/disputes/{{dispute_from_step_3}} \
  -u {{your_chargehound_test_key}}:
var chargehound = require('chargehound')('{{your_chargehound_test_key}}');

chargehound.Disputes.retrieve('{{dispute_from_step_3}}', function (err, res) {
  // ...
});
import chargehound
chargehound.api_key = '{{your_chargehound_test_key}}'

chargehound.Disputes.retrieve('{{dispute_from_step_3}}')
require 'chargehound'
Chargehound.api_key = '{{your_chargehound_test_key}}'

Chargehound::Disputes.retrieve('{{dispute_from_step_3}}')
ch := chargehound.New("{{your_chargehound_test_key}}", nil)

params := chargehound.RetrieveDisputeParams{
  ID: "{{dispute_from_step_3}}",
}

dispute, err := ch.Disputes.Retrieve(&params)
import com.chargehound.Chargehound;

Chargehound chargehound = new Chargehound("{{your_chargehound_test_key}}");

chargehound.disputes.retrieve("{{dispute_from_step_3}}");

5) Using your test API key, you can then update and submit the dispute.

curl https://api.chargehound.com/v1/disputes/{{dispute_from_step_3}}/submit \
  -u {{your_chargehound_test_key}}:
var chargehound = require('chargehound')('{{your_chargehound_test_key}}');

chargehound.Disputes.submit('{{dispute_from_step_3}}', function (err, res) {
  // ...
});
import chargehound
chargehound.api_key = '{{your_chargehound_test_key}}'

chargehound.Disputes.submit('{{dispute_from_step_3}}')
require 'chargehound'
Chargehound.api_key = '{{your_chargehound_test_key}}'

Chargehound::Disputes.submit('{{dispute_from_step_3}}')
ch := chargehound.New("{{your_chargehound_test_key}}", nil)

params := chargehound.UpdateDisputeParams{
  ID: "{{dispute_from_step_3}}",
}

_, err := ch.Disputes.Submit(&params)
import com.chargehound.Chargehound;

Chargehound chargehound = new Chargehound("{{your_chargehound_test_key}}");

chargehound.disputes.submit("{{dispute_from_step_3}}");

Because Chargehound creates live mode disputes with webhooks from Stripe, testing end to end requires creating a dispute in Stripe. You can do this by creating a charge with a test card that simulates a dispute. You can create a charge with a simple curl request, or via the Stripe dashboard.

Testing with Braintree

1) Create a transaction that will trigger a dispute. You can view the resulting dispute in the Braintree dashboard on the disputes page.

gateway.transaction.sale({
  amount: "10.00",
  creditCard: {
    'number': '4023898493988028',
    'expiration_date': '05/2020',
    'cvv': '222'
  },
  options: {
    submitForSettlement: true
  }
}, function (err, result) {
  if (result.success) {
    // See result.transaction for details
  } else {
    // Handle errors
  }
})
braintree.Transaction.sale({
  'amount': '10.00',
  'credit_card': {
      'number': '4023898493988028',
      'expiration_date': '05/2020',
      'cvv': '222'
  },
  'options': {
    'submit_for_settlement': True
  }
})
gateway.transaction.sale(
  :amount => '10.00',
  :credit_card => {
    :number => '4023898493988028',
    :expiration_date => '05/2020',
    :cvv => '222'
  },
  :options => {
    :submit_for_settlement => true
  }
)
TransactionRequest request = new TransactionRequest()
  .amount(new BigDecimal("10.00"))
  .creditCard()
    .number("4023898493988028")
    .expirationDate("05/2020")
    .cvv("222")
  .options()
    .submitForSettlement(true)
    .done();

Result<Transaction> result = gateway.transaction().sale(request);

2) Once the dispute is created in Braintree, you will see it mirrored in Chargehound.

var chargehound = require('chargehound')('{{your_chargehound_test_key}}');

chargehound.Disputes.retrieve('{{dispute_from_step_1}}', function (err, res) {
  // ...
});
import chargehound
chargehound.api_key = '{{your_chargehound_test_key}}'

chargehound.Disputes.retrieve('{{dispute_from_step_1}}')
require 'chargehound'
Chargehound.api_key = '{{your_chargehound_test_key}}'

Chargehound::Disputes.retrieve('{{dispute_from_step_1}}')
import com.chargehound.Chargehound;

Chargehound chargehound = new Chargehound("{{your_chargehound_test_key}}");

chargehound.disputes.retrieve("{{dispute_from_step_1}}");

3) Using your test API key, you can then update and submit the dispute.

var chargehound = require('chargehound')('{{your_chargehound_test_key}}');

chargehound.Disputes.submit('{{dispute_from_step_1}}', function (err, res) {
  // ...
});
import chargehound
chargehound.api_key = '{{your_chargehound_test_key}}'

chargehound.Disputes.submit('{{dispute_from_step_1}}')
require 'chargehound'
Chargehound.api_key = '{{your_chargehound_test_key}}'

Chargehound::Disputes.submit('{{dispute_from_step_1}}')
import com.chargehound.Chargehound;

Chargehound chargehound = new Chargehound("{{your_chargehound_test_key}}");

chargehound.disputes.submit("{{dispute_from_step_1}}");

If you have a Braintree sandbox, you can test your integration using Chargehound's test mode and Braintree's sandox environment. First, you'll need to connect your Braintree sandbox to Chargehound and set up the webhook, just as you did for your production Braintree environment. You can connect a Braintree sandbox from the settings page here.

Because Chargehound creates live mode disputes with webhooks from Braintree, testing end to end requires creating a dispute in Braintree. You can do this by creating a transaction with a test card number that triggers a dispute. You can create a transaction using one of the Braintree SDKs, or via the Braintree dashboard.

Responding to your backlog

Before integrating with Chargehound you might have accrued a dispute backlog, but you can easily respond to all of those disputes by writing a simple script and running it as the final integration step.

curl https://api.chargehound.com/v1/disputes?state=needs_response \
  -u test_123
var chargehound = require('chargehound')(
  'test_123'
);

async function respondToBacklog () {
  var res = await chargehound.Disputes.list({state: 'needs_response'});
  await Promise.all(res.data.map(async function (dispute) {
    // Submit the dispute.
  });

  if (res.has_more) {
    // Recurse to address all of the open disputes.
    await respondToBacklog();
  }
}
import chargehound
chargehound.api_key = 'test_123'

def respond_to_backlog():
  res = chargehound.Disputes.list(state='needs_response')
  for dispute in res['data']:
    # Submit the dispute.

  if res['has_more']:
    # Recurse to address all of the open disputes.
    respond_to_backlog()
require 'chargehound'
Chargehound.api_key = 'test_123'

def respond_to_backlog()
  res = Chargehound::Disputes.list(state: 'needs_response')
  res['data'].each { |dispute|
    # Submit the dispute.
  }

  if res['has_more']
    # Recurse to address all of the open disputes.
    respond_to_backlog()
  end
end
import (
  "github.com/chargehound/chargehound-go"
)

ch := chargehound.New("test_123", nil)

func respondToBacklog () {
  params := chargehound.ListDisputesParams{
    State: "needs_response",
  }

  response, err := ch.Disputes.List(&params)

  for _, dispute := range response.Data {
    // Submit the dispute.
  }

  if response.HasMore == true {
    // Recurse to address all of the open disputes
    respondToBacklog()
  }
}
import com.chargehound.Chargehound;
import com.chargehound.models.DisputesList;
import com.chargehound.models.Dispute;

Chargehound chargehound = new Chargehound("${apiKey}");

public void respondToBacklog() {
  DisputesList result = chargehound.Disputes.list(
    new DisputesList.Params.Builder()
      .state("needs_response")
      .finish()
  );

  for (int i = 0; i < result.data.length; i++) {
    Dispute dispute = result.data[i]
    // Submit the dispute.
  }

  if (result.hasMore) {
    // Recurse to address all of the open disputes.
    respondToBacklog()
  }
}

Webhooks

Webhooks let you register a URL that Chargehound will notify when an event occurs. You might want to use webhooks to be notified when a dispute is created so that you can automatically submit a response. You can configure your webhook URLs on your team settings page, clicking Add webhook URL on that page reveals a form to add a new URL for receiving webhooks. You can select what events you would like to receive a notification for. The events are dispute.created, dispute.updated, dispute.submitted, dispute.closed and dispute.response.generated.

Responding to a webhook

To acknowledge successful receipt of a webhook, your endpoint should return a 2xx HTTP status code. Any other information returned in the request headers or request body is ignored. All response codes outside this range, including 3xx codes, will be treated as a failure. If a webhook is not successfully received for any reason, Chargehound will continue trying to send the webhook once every half hour for up to 3 days.

Dispute created

Notification that Chargehound has received a new dispute from your payment processor.

Example request:

{
  "id": "wh_123",
  "type": "dispute.created",
  "object": "webhook",
  "livemode": true,
  "dispute": "dp_123"
}

The webhook object is:

Field Type Description
id string A unique identifier for the webhook request.
type string The event type.
livemode boolean Is this a test or live mode dispute.
dispute string The id of the dispute.

Dispute updated

Notification that a dispute has been updated.

Example request:

{
  "id": "wh_123",
  "type": "dispute.updated",
  "object": "webhook",
  "livemode": true,
  "dispute": "dp_123"
}

The webhook object is:

Field Type Description
id string A unique identifier for the webhook request.
type string The event type.
livemode boolean Is this a test or live mode dispute.
dispute string The id of the dispute.

Dispute submitted

Notification that a dispute has been submitted.

Example request:

{
  "id": "wh_123",
  "type": "dispute.submitted",
  "object": "webhook",
  "livemode": true,
  "dispute": "dp_123"
}

The webhook object is:

Field Type Description
id string A unique identifier for the webhook request.
type string The event type.
livemode boolean Is this a test or live mode dispute.
dispute string The id of the dispute.

Dispute closed

Notification that a dispute was closed (won, lost, charge_refunded, or warning_closed).

Example request:

{
  "id": "wh_123",
  "type": "dispute.closed",
  "object": "webhook",
  "livemode": true,
  "dispute": "dp_123"
}

The webhook object is:

Field Type Description
id string A unique identifier for the webhook request.
type string The event type.
livemode boolean Is this a test or live mode dispute.
dispute string The id of the dispute.

Dispute response ready

Notification that Chargehound has generated a response for a dispute. This event is typically used for standalone integrations, where you are responsible for uploading the response evidence document yourself.

Example request:

{
  "id": "wh_123",
  "type": "dispute.response.generated",
  "object": "webhook",
  "livemode": true,
  "dispute": "dp_123",
  "charge": "ch_123",
  "account_id": null,
  "evidence": {
    "customer_name": "Susie Chargeback"
  },
  "response_url": "https://chargehound.s3.amazonaws.com/XXX.pdf?Signature=XXX&Expires=XXX&AWSAccessKeyId=XXX"
}

The webhook object is:

Field Type Description
id string A unique identifier for the webhook request.
type string The event type.
livemode boolean Is this a test or live mode dispute.
dispute string The id of the dispute.
charge string The id of the disputed charge.
response_url string The URL of the generated response PDF. This URL is a temporary access URL.
evidence dictionary Key value pairs for the dispute response evidence object.
account_id string The account id for Connected accounts that are charged directly through Stripe (if any). (See Stripe charging directly for details.)

Standalone Integration

In typical connected integrations Chargehound has third party access to your payment processor. This allows Chargehound to automatically sync your disputes as they are created, update your disputes with relevant information, and upload the response to your payment processor after you submit a dispute. A connected integration is the least effort for you, however, in some cases a connected integration may not be possible or desired.

A standalone integration gives you the responsibilty and control over creating disputes in Chargehound and uploading the generated response to your payment processor when it is ready. You will create a dispute via API and when the response is ready you will receive a dispute.response.generated webhook notification from Chargehound. You can then fetch the response information, including the PDF document generated from your template, and upload the response to your payment processor.

Creating a dispute via API

In a standalone integration, you will need to create a dispute in Chargehound when you receive a notification from your payment processor.

Definition:

POST /v1/disputes
chargehound.Disputes.create();
chargehound.Disputes.create()
Chargehound::Disputes.create
ch.Disputes.Create(*chargehound.CreateDisputeParams)
chargehound.disputes.create();

Example request:

curl -X POST https://api.chargehound.com/v1/disputes?submit=true \
  -u test_123: \
  -d template=unrecognized \
  -d fields[customer_name]="Susie Chargeback" \
  -d id=dp_123 \
  -d charge=ch_123 \
  -d customer=cus_123 \
  -d processor=stripe \
  -d reason=unrecognized \
  -d charged_at="2016-10-01T22:20:53" \
  -d disputed_at="2016-10-01T22:20:53" \
  -d due_by="2016-12-01T22:20:53" \
  -d currency=usd \
  -d amount=500 \
  -d reversal_currency=usd \
  -d fee=1500 \
  -d reversal_amount=500
var chargehound = require('chargehound')(
  'test_123'
);

chargehound.Disputes.create({
  template: 'unrecognized',
  fields: {
    customer_name: 'Susie Chargeback'
  },
  id: 'dp_123',
  charge: 'ch_123',
  customer: 'cus_123',
  processor: 'stripe',
  reason: 'unrecognized',
  charged_at: '2016-10-01T22:20:53',
  disputed_at: '2016-10-01T22:20:53',
  due_by: '2016-12-01T22:20:53',
  currency: 'usd',
  amount: 500,
  reversal_currency: 'usd',
  fee: 1500,
  reversal_amount: 500
}, function (err, res) {
  // ...
});
import chargehound
chargehound.api_key = 'test_123'

chargehound.Disputes.create(
  template = 'unrecognized',
  fields = {
    customer_name: 'Susie Chargeback'
  },
  id = 'dp_123',
  charge = 'ch_123',
  customer = 'cus_123',
  processor = 'stripe',
  reason = 'unrecognized',
  charged_at = '2016-10-01T22 =20 =53',
  disputed_at = '2016-10-01T22 =20 =53',
  due_by = '2016-12-01T22 =20 =53',
  currency = 'usd',
  amount = 500,
  reversal_currency = 'usd',
  fee = 1500,
  reversal_amount = 500
)
require 'chargehound'
Chargehound.api_key = 'test_123'

Chargehound::Disputes.create(
  template: 'unrecognized',
  fields: {
    customer_name => 'Susie Chargeback'
  },
  id: 'dp_123',
  charge: 'ch_123',
  customer: 'cus_123',
  processor: 'stripe',
  reason: 'unrecognized',
  charged_at: '2016-10-01T22:20:53',
  disputed_at: '2016-10-01T22:20:53',
  due_by: '2016-12-01T22:20:53',
  currency: 'usd',
  amount: 500,
  reversal_currency: 'usd',
  fee: 1500,
  reversal_amount: 500
)
import (
  "github.com/chargehound/chargehound-go"
)

ch := chargehound.New("test_123", nil)

params := chargehound.CreateDisputeParams{
  Template: "unrecognized",
  Fields: map[string]interface{}{
    "customer_name": "Susie Chargeback",
  },
  ID: "dp_123",
  Charge: "ch_123",
  Customer: "cus_123",
  Processor: "stripe",
  Reason: "unrecognized",
  ChargedAt: "2016-10-01T22:20:53",
  DisputedAt: "2016-10-01T22:20:53",
  DueBy: "2016-12-01T22:20:53",
  Currency: "usd",
  Amount: 500,
  ReversalCurrency: "usd",
  Fee: 1500,
  ReversalAmount: 500,
}

dispute, err := ch.Disputes.Create(&params)
import com.chargehound.Chargehound;
import com.chargehound.models.Dispute;

Chargehound chargehound = new Chargehound("test_123");

Map<String, Object> fields = new HashMap<String, Object>();
fields.put("customer_name", "Susie Chargeback");

chargehound.disputes.create(
  new Dispute.CreateParams.Builder()
    .template("unrecognized")
    .fields(fields)
    .id("dp_123")
    .charge("ch_123")
    .customer("cus_123")
    .processor("stripe")
    .reason("general")
    .chargedAt("2016-10-01T22:20:53")
    .disputedAt("2016-10-01T22:20:53")
    .dueBy("2016-12-01T22:20:53")
    .currency("usd")
    .amount(500)
    .reversalCurrency("usd")
    .fee(1500)
    .reversalAmount(500)
    .finish()
);

Example response:

{
  "customer": "cus_123",
  "livemode": false,
  "currency": "usd",
  "missing_fields": {},
  "address_zip_check": "pass",
  "id": "dp_123",
  "customer_name": "Susie Chargeback",
  "fee": 1500,
  "reversal_amount": 500,
  "due_by": "2016-12-01T22:20:53",
  "state": "needs_response",
  "statement_descriptor": null,
  "source": "api",
  "charge": "ch_123",
  "reference_url": null,
  "template": "unrecognized",
  "is_charge_refundable": false,
  "cvc_check": "unavailable",
  "customer_email": null,
  "account_id": null,
  "address_line1_check": "pass",
  "object": "dispute",
  "customer_purchase_ip": null,
  "disputed_at": "2016-10-01T22:20:53",
  "submitted_count": 0,
  "reason": "unrecognized",
  "reversal_total": 2000,
  "charged_at": "2016-10-01T22:20:53",
  "reversal_currency": "usd",
  "address_zip": null,
  "url": "/v1/disputes/dp_123",
  "fields": {
    "customer_name": "Susie Chargeback"
  },
  "amount": 500,
  "products": [],
  "processor": "stripe"
}

Parameters

Parameter Type Required? Description
id string required The id of the dispute in your payment processor.
charge string required The id of the disputed charge in your payment processor.
customer string optional The id of the charged customer in your payment processor.
reason string required The bank provided reason for the dispute. One of general, fraudulent, duplicate, subscription_canceled, product_unacceptable, product_not_received, unrecognized, credit_not_processed, incorrect_account_details, insufficient_funds, bank_cannot_process, debit_not_authorized, goods_services_returned_or_refused, goods_services_cancelled, transaction_amount_differs, retrieved.
charged_at string required ISO 8601 timestamp - when the charge was made.
disputed_at string required ISO 8601 timestamp - when the charge was disputed.
due_by string required ISO 8601 timestamp - when dispute evidence needs to be disputed by.
currency string required The currency code of the disputed charge. e.g. 'USD'.
amount integer required The amount of the disputed charge. Amounts are in cents (or other minor currency unit.)
processor string optional The payment processor for the charge. One of braintree, vantiv, adyen, worldpay or stripe.
state string optional The state of the dispute. One of needs_response, warning_needs_response.
reversal_currency string optional The currency code of the dispute balance withdrawal. e.g. 'USD'.
fee integer optional The amount of the dispute fee. Amounts are in cents (or other minor currency unit.)
reversal_amount integer optional The amount of the dispute balance withdrawal (without fee). Amounts are in cents (or other minor currency unit.)
reversal_total integer optional The total amount of the dispute balance withdrawal (with fee). Amounts are in cents (or other minor currency unit.)
is_charge_refundable boolean optional Is the disputed charge refundable.
submitted_count integer optional How many times has dispute evidence been submitted.
address_line1_check string optional State of address check (if available). One of pass, fail, unavailable, checked.
address_zip_check string optional State of address zip check (if available). One of pass, fail, unavailable, checked.
cvc_check string optional State of cvc check (if available). One of pass, fail, unavailable, checked.
template string optional The id of the template to use.
fields dictionary optional Key value pairs to hydrate the template's evidence fields.
products array optional List of products the customer purchased. (See Product data for details.)
reference_url string optional Custom URL with dispute information, such as the dispute or charge in your company dashboard.
account_id string optional Set the account id for Connected accounts that are charged directly through Stripe. (See Stripe charging directly for details.)
kind string optional Type of dispute (if available). One of chargeback, retrieval, pre_arbitration.
submit boolean optional Submit dispute evidence immediately after creation.
queue boolean optional Queue the dispute for submission on its due date. (See Queuing for submission for details.)
force boolean optional Skip the manual review filters or submit a dispute in manual review. (See Manual review for details.)

Possible errors

Error code Description
400 Bad Request Dispute is missing data, or is missing fields required by the template.

Retrieving a dispute response

Once the response is generated, you can fetch the response data from the Chargehound API.

Definition:

GET /v1/disputes/{{dispute_id}}/response
chargehound.Disputes.response();
chargehound.Disputes.response()
Chargehound::Disputes.response
ch.Disputes.Response(*chargehound.RetrieveDisputeParams)
chargehound.disputes.response();

Example request:

curl https://api.chargehound.com/v1/disputes/dp_123/response \
  -u test_123:
var chargehound = require('chargehound')(
  'test_123'
);

chargehound.Disputes.response('dp_123', function (err, res) {
  // ...
});
import chargehound
chargehound.api_key = 'test_123'

chargehound.Disputes.response('dp_123')
require 'chargehound'
Chargehound.api_key = 'test_123'

Chargehound::Disputes.response('dp_123')
import (
  "github.com/chargehound/chargehound-go"
)

ch := chargehound.New("test_123", nil)

params := chargehound.RetrieveDisputeParams{
  ID: "dp_123",
}

response, err := ch.Disputes.Response(&params)
import com.chargehound.Chargehound;

Chargehound chargehound = new Chargehound("test_123");

chargehound.disputes.response("dp_123");

Example response:

{
  "object": "response",
  "livemode": true,
  "dispute": "dp_123",
  "external_identifier": "ch_123",
  "account_id": null,
  "evidence": {
    "customer_name": "Susie Chargeback"
  },
  "response_url": "https://chargehound.s3.amazonaws.com/XXX.pdf?Signature=XXX&Expires=XXX&AWSAccessKeyId=XXX"
}

The response object is:

Field Type Description
dispute string The id of the dispute.
charge string The id of the disputed charge.
response_url string The URL of the generated response PDF. This URL is a temporary access URL.
evidence dictionary Key value pairs for the dispute response evidence object.
account_id string The account id for Connected accounts that are charged directly through Stripe (if any). (See Stripe charging directly for details.)