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",
"type": "dispute_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. |
Error details
An API error has a few standard fields in the "error" JSON object. These fields are "status", "message", and "type".
"status" is the HTTP status code of the error.
"message" is a human readable description of the error. This is the place to look when debugging.
"type" is a structured error type string. This can help you programmatically handle some errors.
Error types
Type | Description |
---|---|
authentication | Invalid API key provided. |
dispute_closed | Dispute won/lost and cannot be updated. |
dispute_not_found | No dispute found for the given ID. |
dispute_overdue | Dispute overdue and cannot be submitted. |
dispute_submitted | Dispute already submitted and cannot be submitted again. |
dispute_uneditable | Dispute cannot be updated. |
invalid_correspondence | Invalid correspondence data. |
invalid_fields | Invalid fields data. |
invalid_past_payments | Invalid past payments data. |
invalid_products | Invalid products data. |
invalid_request | The request data was invalid or incomplete. |
missing_fields | Missing fields required by template. |
missing_template | Template not set for dispute. |
template_not_found | No template found for the given ID. |
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.
Response metadata
Our API responses contain a few standard metadata fields in the JSON. These fields are "object", "livemode", and "url".
"object" identifies the type of object returned by the API. Currently the object can be a "dispute", "list", or "webhook". A list contains a list of other objects in the "data" field.
"livemode" shows whether the API returned test or live data. The mode is determined by which API key was used to authenticate.
"url" shows the path of the request that was made.
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:
- Removing response attributes from an existing resource
- Removing request parameters from an existing resource
Examples of backwards-compatible changes:
- Adding new API endpoints
- Adding new optional response attributes to an existing resource
- Adding new optional request attributes
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 2021-09-15
In this API version, we changed the behavior of the webhook dispute.closed
event.
In older versions, a dispute.closed
notification would only be sent if Chargehound submitted the dispute. Starting with this version, the dispute.closed
notification will be sent for all closed disputes, whether Chargehound submitted evidence or not.
Version 2020-02-28
In this API version, we changed the behavior of the accept filters and the accepted
state.
In older versions, workflow rules for accepting disputes were only applied to new disputes, and disputes
in the accepted
state could be submitted normally by API requests. Accepted disputes were
intended to help with dashboard organization and did not affect API integrations.
Starting with this version, workflow rules for accepting disputes are also applied when disputes are
updated or submitted via API, and disputes in the accepted
state cannot be submitted by API requests
without the force
parameter. Accepted disputes are intended to complement manual review filters.
Version 2017-10-30
In this API version, we’ve cleaned up some attribute names in order to make them more consistent and intuitive.
Dispute response webhook: The
dispute_id
attribute was removed in favor ofdispute
. Theexternal_charge
attribute was removed in favor ofcharge
. Theuser_id
attribute was removed in favor ofaccount_id
.Dispute response endpoint: The
dispute_id
attribute was removed in favor ofdispute
. Theexternal_charge
attribute was removed in favor ofcharge
. Theuser_id
attribute was removed in favor ofaccount_id
.Dispute create endpoint: The
external_identifier
attribute was removed in favor ofid
. Theexternal_charge
attribute was removed in favor ofcharge
. Theexternal_customer
attribute was removed in favor ofcustomer
. Theuser_id
parameter was removed in favor ofaccount_id
.Dispute submit endpoint and dispute update endpoint: The
customer_name
parameter was removed, set thecustomer_name
in the fields object instead. Thecustomer_email
parameter was removed, set thecustomer_email
in the fields object instead. Theuser_id
parameter was removed in favor ofaccount_id
.
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.) |
correspondence | array | A list of communications with the customer. (See Customer correspondence 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 , unchecked . |
address_zip_check | string | State of address zip check (if available). One of pass , fail , unavailable , unchecked . |
cvc_check | string | State of cvc check (if available). One of pass , fail , unavailable , unchecked . |
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 , paypal , amex or stripe |
processor | string | The payment processor of the dispute. One of braintree , vantiv , adyen , worldpay , paypal , amex 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(¶ms)
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": [],
"past_payments": [],
"correspondence": [],
"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.) |
correspondence | array | optional | A list of communications with the customer. (See Customer correspondence for details.) |
past_payments | array | optional | History of the customer's valid, non-disputed transactions using the same card. (See Past payments 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?state=warning_needs_response&state=needs_response \
-u test_123:
var chargehound = require('chargehound')(
'test_123'
);
chargehound.Disputes.list({state: ['warning_needs_response', 'needs_response']}, function (err, res) {
// ...
});
import chargehound
chargehound.api_key = 'test_123'
chargehound.Disputes.list(state=['warning_needs_response', 'needs_response'])
require 'chargehound'
Chargehound.api_key = 'test_123'
Chargehound::Disputes.list(state: %w[warning_needs_response needs_response])
import (
"github.com/chargehound/chargehound-go"
)
ch := chargehound.New("test_123", nil)
disputeList, err := ch.Disputes.List(&chargehound.ListDisputesParams{
State: []string{
"warning_needs_response",
"needs_response",
},
})
import com.chargehound.Chargehound;
import com.chargehound.models.DisputesList;
Chargehound chargehound = new Chargehound("test_123");
DisputesList.Params params = new DisputesList.Params.Builder()
.state("warning_needs_response", "needs_response")
.finish();
DisputesList result = chargehound.disputes.list(params);
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": [],
"past_payments": [],
"correspondence": [],
"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. Filter the disputes by state. Multiple state parameters can be provided to expand the filter to multiple states. |
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(¶ms)
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": [],
"past_payments": [],
"correspondence": [],
"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(¶ms)
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": [],
"past_payments": [],
"correspondence": [],
"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.) |
correspondence | array | optional | A list of communications with the customer. (See Customer correspondence for details.) |
past_payments | array | optional | History of the customer's valid, non-disputed transactions using the same card. (See Past payments 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(¶ms)
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": [],
"past_payments": [],
"correspondence": [],
"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 intended 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\" : \"https://static.chargehound.com/saxophone.png\",
\"sku\" : \"17283001272\",
\"quantity\" : 1,
\"amount\" : 20000,
\"url\" : \"http://www.example.com\",
\"shipping_carrier\": \"fedex\",
\"shipping_tracking_number\": \"657672264372\"
},{
\"name\" : \"Milk\",
\"description\" : \"Semi-skimmed Organic\",
\"image\" : \"https://static.chargehound.com/milk.png\",
\"sku\" : \"26377382910\",
\"quantity\" : \"64oz\",
\"amount\" : 400,
\"url\" : \"http://www.example.com\",
\"shipping_carrier\": \"fedex\",
\"shipping_tracking_number\": \"657672264372\"
}]"
var chargehound = require('chargehound')(
'test_123'
);
chargehound.Disputes.update('dp_123', {
products: [{
'name': 'Saxophone',
'description': 'Alto saxophone, with carrying case',
'image': 'https://static.chargehound.com/saxophone.png',
'sku': '17283001272',
'quantity': 1,
'amount': 20000,
'url': 'http://www.example.com',
'shipping_carrier': 'fedex',
'shipping_tracking_number': '657672264372'
},{
'name': 'Milk',
'description': 'Semi-skimmed Organic',
'image': 'https://static.chargehound.com/milk.png',
'sku': '26377382910',
'quantity': '64oz',
'amount': 400,
'url': 'http://www.example.com',
'shipping_carrier': 'fedex',
'shipping_tracking_number': '657672264372'
}]
}, 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': 'https://static.chargehound.com/saxophone.png',
'sku': '17283001272',
'quantity': 1,
'amount': 20000,
'url': 'https://www.example.com',
'shipping_carrier': 'fedex',
'shipping_tracking_number': '657672264372'
}, {
'name': 'Milk',
'description': 'Semi-skimmed Organic',
'image': 'https://static.chargehound.com/milk.png',
'sku': '26377382910',
'quantity': '64oz',
'amount': 400,
'url': 'https://www.example.com',
'shipping_carrier': 'fedex',
'shipping_tracking_number': '657672264372'
}]
)
require 'chargehound'
Chargehound.api_key = 'test_123'
Chargehound::Disputes.update('dp_123',
products: [{
'name' => 'Saxophone',
'description' => 'Alto saxophone, with carrying case',
'image' => 'https://static.chargehound.com/saxophone.png',
'sku' => '17283001272',
'quantity' => 1,
'amount' => 20000,
'url' => 'https://www.example.com'
},{
'name' => 'Milk',
'description' => 'Semi-skimmed Organic',
'image' => 'https://static.chargehound.com/milk.png',
'sku' => '26377382910',
'quantity' => '64oz',
'amount' => 400,
'url' => 'https://www.example.com'
}]
)
import (
"github.com/chargehound/chargehound-go"
)
ch := chargehound.New("test_123", nil)
params := chargehound.UpdateDisputeParams{
ID: "dp_123",
Products: []chargehound.Product{
{
Name: "Saxophone",
Description: "Alto saxophone, with carrying case",
Image: "https://static.chargehound.com/saxophone.png",
Sku: "17283001272",
Quantity: 1,
Amount: 20000,
Url: "http://www.example.com",
ShippingCarrier: "fedex",
ShippingTrackingNumber: "657672264372",
},
{
Name: "Milk",
Description: "Semi-skimmed Organic",
Image: "https://static.chargehound.com/milk.png",
Sku: "26377382910",
Quantity: "64oz",
Amount: 400,
Url: "http://www.example.com",
ShippingCarrier: "fedex",
ShippingTrackingNumber: "657672264372",
},
},
}
dispute, err := ch.Disputes.Update(¶ms)
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("https://static.chargehound.com/saxophone.png")
.sku("17283001272")
.quantity(1)
.amount(20000)
.url("http://www.example.com")
.shippingCarrier("fedex")
.shippingTrackingNumber("657672264372")
.finish();
Product milkProduct = new Product.Builder()
.name("Milk")
.description("Semi-skimmed Organic")
.image("https://static.chargehound.com/milk.png")
.sku("26377382910")
.quantity("64oz")
.amount(400)
.url("http://www.example.com")
.shippingCarrier("fedex")
.shippingTrackingNumber("657672264372")
.finish();
List<Product> products = new ArrayList<Product>();
products.add(saxophoneProduct);
products.add(milkProduct);
chargehound.disputes.update("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. |
shipping_carrier | string | optional | Shipping carrier for the shipment for the product. |
shipping_tracking_number | string | optional | Shipping tracking number for the shipment for the product. |
Customer correspondence
If you have a record of email communication with the customer, you can attach that record to a dispute when updating or submitting the dispute. Each correspondence item has the following properties:
Example usage:
curl -X PUT https://api.chargehound.com/v1/disputes/dp_123 \
-u test_123: \
-d correspondence="[{ \
\"to\": \"customer@example.com\", \
\"from\": \"noreply@example.com\", \
\"sent\": \"2019-03-31 09:00:22PM UTC\", \
\"subject\": \"Your Order\", \
\"body\": \"Your order was received.\", \
\"caption\": \"Order confirmation email.\" \
}, { \
\"to\": \"customer@example.com\", \
\"from\": \"noreply@example.com\", \
\"sent\": \"2019-04-03 08:59:36PM UTC\", \
\"subject\": \"Your Order\", \
\"body\": \"Your order was delivered.\", \
\"caption\": \"Delivery confirmation email.\" \
}]"
var chargehound = require('chargehound')(
'test_123'
);
chargehound.Disputes.update('dp_123', {
correspondence: [{
'to': 'customer@example.com',
'from': 'noreply@example.com',
'sent': '2019-03-31 09:00:22PM UTC',
'subject': 'Your Order',
'body': 'Your order was received.',
'caption': 'Order confirmation email.'
}, {
'to': 'customer@example.com',
'from': 'noreply@example.com',
'sent': '2019-04-01 09:00:22PM UTC',
'subject': 'Your Order',
'body': 'Your order was delivered.',
'caption': 'Delivery confirmation email.'
}]
}, function (err, res) {
// ...
});
import chargehound
chargehound.api_key = 'test_123'
chargehound.Disputes.update('dp_123',
correspondence=[{
'to': 'customer@example.com',
'from': 'noreply@example.com',
'sent': '2019-03-31 09:01:01PM UTC',
'subject': 'Your Order',
'body': 'Your order was received.',
'caption': 'Order confirmation email.'
}, {
'to': 'customer@example.com',
'from': 'noreply@example.com',
'sent': '2019-04-01 09:01:01PM UTC',
'subject': 'Your Order',
'body': 'Your order was delivered.',
'caption': 'Delivery confirmation email.'
}]
)
require 'chargehound'
Chargehound.api_key = 'test_123'
Chargehound::Disputes.update('dp_123',
correspondence: [{
'to' => 'customer@example.com',
'from' => 'noreply@example.com',
'sent' => '2019-03-31 09:01:26PM UTC',
'subject' => 'Your Order',
'body' => 'Your order was received.',
'caption' => 'Order confirmation email.'
}, {
'to' => 'customer@example.com',
'from' => 'noreply@example.com',
'sent' => '2019-04-01 09:01:26PM UTC',
'subject' => 'Your Order',
'body' => 'Your order was delivered.',
'caption' => 'Delivery confirmation email.'
}]
)
import (
"github.com/chargehound/chargehound-go"
)
ch := chargehound.New("test_123", nil)
params := chargehound.UpdateDisputeParams{
ID: "dp_123",
Correspondence: []chargehound.CorrespondenceItem{
{
To: "customer@example.com",
From: "noreply@example.com",
Sent: "2019-03-31 09:04:05PM UTC",
Subject: "Your Order",
Body: "Your order was received.",
Caption: "Order confirmation email."
},
{
To: "customer@example.com",
From: "noreply@example.com",
Sent: "2019-04-01 09:04:05PM UTC",
Subject: "Your Order",
Body: "Your order was delivered.",
Caption: "Delivery confirmation email."
},
},
}
dispute, err := ch.Disputes.Update(¶ms)
import com.chargehound.Chargehound;
import com.chargehound.models.Dispute;
import com.chargehound.models.Email;
Chargehound chargehound = new Chargehound("test_123");
Email confirmationEmail = new Email.Builder()
.to("customer@example.com")
.from("noreply@example.com")
.sent("2019-03-31 09:04:55PM UTC")
.subject("Your Order")
.body("Your order was received.")
.caption("Order confirmation email.")
.finish();
Email deliveryEmail = new Email.Builder()
.to("customer@example.com")
.from("noreply@example.com")
.sent("2019-04-01 09:04:55PM UTC")
.subject("Your Order")
.body("Your order was delivered.")
.caption("Delivery confirmation email.")
.finish();
List<Email> correspondence = new ArrayList<Email>();
correspondence.add(confirmationEmail);
correspondence.add(deliveryEmail);
chargehound.disputes.update("dp_123",
new Dispute.UpdateParams.Builder()
.correspondence(correspondence)
.finish()
);
Correspondence item fields
Field | Type | Required? | Description |
---|---|---|---|
to | string | required | The address where the email was sent. E.g. the customer's email address. |
from | string | required | The address of the email sender. E.g. your company's support email address. |
sent | string | optional | When the email was sent. |
subject | string | required | The email subject line. |
body | string | required | The email body, as plain text. |
caption | string | optional | A description of the email. |
Past payments
Showing a history of valid transactions with a customer can serve as evidence that their disputed transaction was also a valid transaction. Typically, Chargehound can automatically fetch past payments from your payment processor. Generally, you do not need to set past payment information yourself.
The past payments provided to our API should be successful, non-disputed transactions that used the same credit card as the disputed transaction. The past payment list should not include more than 10 payments. You can update the past payment history when updating or submitting the dispute. Each payment has the following properties:
Example usage:
curl -X PUT https://api.chargehound.com/v1/disputes/dp_123 \
-u test_123: \
-d past_payments="[{ \
\"id\": \"ch_1\", \
\"amount\": 20000, \
\"currency\": \"usd\", \
\"charged_at\": \"2019-09-10 10:18:41PM UTC\" \
}, { \
\"id\": \"ch_2\", \
\"amount\": 50000, \
\"currency\": \"usd\", \
\"charged_at\": \"2019-09-03 10:18:41PM UTC\" \
}]"
var chargehound = require('chargehound')(
'test_123'
);
chargehound.Disputes.update('dp_123', {
past_payments: [{
'id': 'ch_1',
'amount': 20000,
'currency': 'usd',
'charged_at': '2019-09-10 11:09:41PM UTC'
}, {
'id': 'ch_2',
'amount': 50000,
'currency': 'usd',
'charged_at': '2019-09-03 11:09:41PM UTC'
}]
}, function (err, res) {
// ...
});
import chargehound
chargehound.api_key = 'test_123'
chargehound.Disputes.update('dp_123',
past_payments = [{
'id': 'ch_1',
'amount': 20000,
'currency': 'usd',
'charged_at': '2019-09-10 11:10:06PM UTC'
}, {
'id': 'ch_2',
'amount': 50000,
'currency': 'usd',
'charged_at': '2019-09-03 11:10:06PM UTC'
}]
)
require 'chargehound'
Chargehound.api_key = 'test_123'
Chargehound::Disputes.update('dp_123',
past_payments: [{
'id' => 'ch_1',
'amount' => 20000,
'currency' => 'usd',
'charged_at' => '2019-09-10 11:10:14PM UTC'
}, {
'id' => 'ch_2',
'amount' => 50000,
'currency' => 'usd',
'charged_at' => '2019-09-03 11:10:14PM UTC'
}]
)
import (
"github.com/chargehound/chargehound-go"
)
ch := chargehound.New("test_123", nil)
params := chargehound.UpdateDisputeParams{
ID: "dp_123",
PastPayments: []chargehound.PastPayment{
{
ID: "ch_1",
Amount: 20000,
Currency: "usd",
ChargedAt: "2019-09-10 11:10:22PM UTC",
},
{
ID: "ch_2",
Amount: 50000,
Currency: "usd",
ChargedAt: "2019-09-03 11:10:22PM UTC",
},
},
}
dispute, err := ch.Disputes.Update(¶ms)
import com.chargehound.Chargehound;
import com.chargehound.models.Dispute;
import com.chargehound.models.PastPayment;
Chargehound chargehound = new Chargehound("test_123");
PastPayment firstPayment = new PastPayment.Builder()
.id("ch_1")
.amount(20000)
.currency("usd")
.chargedAt("2019-09-10 11:10:47PM UTC")
.finish();
PastPayment secondPayment = new PastPayment.Builder()
.id("ch_2")
.amount(50000)
.currency("usd")
.chargedAt("2019-09-03 11:10:47PM UTC")
.finish();
List<PastPayment> pastPayments = new ArrayList<PastPayment>();
pastPayments.add(firstPayment);
pastPayments.add(secondPayment);
chargehound.disputes.update("dp_123",
new Dispute.UpdateParams.Builder()
.pastPayments(pastPayments)
.finish()
);
Past payment fields
Field | Type | Required? | Description |
---|---|---|---|
id | string | required | The ID of the transaction in your payment processor. |
amount | integer | required | The amount of the transaction, in cents (or other minor currency unit.) |
currency | string | required | A 3 character ISO currency code. |
charged_at | string or integer | required | The date of the transaction, as a formatted string or Unix timestamp. |
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(¶ms)
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(¶ms)
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(¶ms)
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.
Linking data
// Use the charge ID to look up payment data.
var charge = dispute.charge
// Order ID may help find payment data.
var order = dispute.fields['order_id']
# Use the charge ID to look up payment data.
charge = dispute.charge
# Order ID may help find payment data.
order = dispute.fields['order_id']
# Use the charge ID to look up payment data.
charge = dispute.charge
# Order ID may help find payment data.
order = dispute.fields['order_id']
// Use the charge ID to look up payment data.
charge := dispute.charge
// Order ID may help find payment data.
order := dispute.fields["order_id"]
// Use the charge ID to look up payment data.
String charge = dispute.charge;
// Order ID may help find payment data.
String order = dispute.fields.get("order");
The first step in responding to a dispute with Chargehound is linking the dispute to key data in your system. The exact details of this vary by payment processor and company. Here are the most common IDs you will use to link data:
charge: The
charge
property of a Chargehound dispute is the payment ID used by the payment processor. You can use this ID to look up payment data in your system.customer: The
customer
property of a Chargehound dispute is the customer ID used by the payment processor (if available). You can use this ID to look up customer data in your system.order_id:
order_id
may be available in thefields
hash of a Chargehound dispute. The meaning of theorder_id
varies, in general it's an order, receipt, or payment ID. If available, you can use theorder_id
to look up payment data in your system.
Collecting evidence
You will need to collect specific evidence in order to submit a dispute. The fields
hash on a dispute represents this evidence. The exact fields depend on your template. To understand the data you will need to collect, go to the templates page and click "View details" to see customized documentation for a template.
On the template page, click the "Fields" tab to see the overall list of fields for the template. Required fields are needed to submit a response with the template. Optional fields represent additional information that is good to have, but not necessary. You should set a value for as many of the optional fields as possible, but they are not required to submit a response.
Click on the template's "Integration" tab to see an example of how to update the fields and submit the dispute using Chargehound's API. 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. The "Collecting Evidence" tool helps you check what fields you will need to collect.
Formatting fields
You will encounter a few different types of fields. To view the types of your fields, use the "Fields" tab of the template page described above. 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 or integer | Submitted responses will be reviewed by humans so try to format dates to be human readable and friendly, although no specific format is enforced. You can also provide a Unix timestamp, which will be formatted by Chargehound. |
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:// ). |
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 list 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.
Workflow rules and automation
Chargehound has a number of workflow automation rules that can act on a dispute using its data. You can view those here. Instead of coding all of the logic to decide what to do with a dispute, it often makes more sense to send as much data as possible for a dispute, and then a dashboard user can manage the rules to automate the workflow. For example, instead of setting a template on each of your submit API calls, you can use the Template Select workflow rules in the dashboard. By using the rules, you can avoid writing and maintaining code when you need to make changes to your workflow or add a new template.
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.
Handling 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(¶ms)
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(¶ms)
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. If you have a test environment, you can create a charge there to simulate a dispute end to end in your system. You can also 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 sandbox environment. First, you'll need to connect your Braintree sandbox to Chargehound, just as you did for your production Braintree environment. You can connect a Braintree sandbox account 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. If you have a test environment, you can create a transaction there to simulate a dispute end to end in your system. You can also create a transaction using one of the Braintree SDKs, or via the Braintree dashboard.
Testing with PayPal
1) Use the Orders API to charge a test buyer account from the sandbox PayPal account that you connected to Chargehound.
curl https://api.sandbox.paypal.com/v2/checkout/orders \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {{your_paypal_access_key}}" \
-d '{
"payer": {
"email_address": "{{your_buyer_account_email_address}}"
},
"intent": "CAPTURE",
"purchase_units": [
{
"amount": {
"currency_code": "USD",
"value": "100.00"
}
}
]
}'
2) As the Paypal buyer account, approve the payment using the link returned in the response from step 1. It is the "approve" link and should look like
https://www.sandbox.paypal.com/checkoutnow?token={{token}}
.3) Using your Paypal facilitator API key, capture the payment.
curl-X POST https://api.sandbox.paypal.com/v2/checkout/orders/{{order_id_from_step_1}}/capture \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {{your_paypal_access_key}}"
4) As the Paypal buyer account, find the transaction in your activity. Click "Report a problem". Click "I want to report unauthorized activity". Fill out any details, it's not important what they are. You can skip the change password step. You can view the resulting dispute in the resolution center.
5) You can fetch the dispute in Chargehound using the Paypal ID. Remember, it takes on average 3-5 hours for the dispute to appear in Chargehound.
curl https://api.chargehound.com/v1/disputes/{{dispute_from_step_4}} \
-u {{your_chargehound_test_key}}:
var chargehound = require('chargehound')('{{your_chargehound_test_key}}');
chargehound.Disputes.retrieve('{{dispute_from_step_4}}', function (err, res) {
// ...
});
import chargehound
chargehound.api_key = '{{your_chargehound_test_key}}'
chargehound.Disputes.retrieve('{{dispute_from_step_4}}')
require 'chargehound'
Chargehound.api_key = '{{your_chargehound_test_key}}'
Chargehound::Disputes.retrieve('{{dispute_from_step_4}}')
ch := chargehound.New("{{your_chargehound_test_key}}", nil)
params := chargehound.RetrieveDisputeParams{
ID: "{{dispute_from_step_4}}",
}
dispute, err := ch.Disputes.Retrieve(¶ms)
import com.chargehound.Chargehound;
Chargehound chargehound = new Chargehound("{{your_chargehound_test_key}}");
chargehound.disputes.retrieve("{{dispute_from_step_4}}");
6) Using your test API key, you can then update and submit the dispute.
curl https://api.chargehound.com/v1/disputes/{{dispute_from_step_4}}/submit \
-u {{your_chargehound_test_key}}:
var chargehound = require('chargehound')('{{your_chargehound_test_key}}');
chargehound.Disputes.submit('{{dispute_from_step_4}}', function (err, res) {
// ...
});
import chargehound
chargehound.api_key = '{{your_chargehound_test_key}}'
chargehound.Disputes.submit('{{dispute_from_step_4}}')
require 'chargehound'
Chargehound.api_key = '{{your_chargehound_test_key}}'
Chargehound::Disputes.submit('{{dispute_from_step_4}}')
ch := chargehound.New("{{your_chargehound_test_key}}", nil)
params := chargehound.UpdateDisputeParams{
ID: "{{dispute_from_step_4}}",
}
_, err := ch.Disputes.Submit(¶ms)
import com.chargehound.Chargehound;
Chargehound chargehound = new Chargehound("{{your_chargehound_test_key}}");
chargehound.disputes.submit("{{dispute_from_step_4}}");
If you have a Paypal sandbox, you can test your integration using Chargehound's test mode and Paypal's sandbox environment. First, you'll need to connect your Paypal sandbox to Chargehound, just as you did for your production Paypal environment. You can connect a Paypal sandbox account from the settings page here.
Testing end to end requires creating a dispute in Paypal. You can do this by creating a transaction from a sandbox PayPal buyer account and disputing the transaction. Be sure to follow the steps given here exactly. It is important that you choose the correct reason for filing a dispute. You want to create a Chargeback in Paypal. Some dispute reasons will create Paypal Inquiries rather than Chargebacks.
Be prepared to wait after creating the dispute in Paypal. Unfortunately, Chargehound cannot sync Paypal disputes in real time. Transaction data is not immediately available to us in the Paypal API we use. After a dispute is created in Paypal, you will have to wait between 3-5 hours before it is available in Chargehound. When testing with Paypal's sandbox, it can take even longer.
Testing with Checkout
1) Create a payment with a dispute trigger.
2) Once the dispute is created in Checkout, you will see it mirrored in Chargehound.
curl https://api.chargehound.com/v1/disputes/{{dispute_from_step_1}} \
-u {{your_chargehound_test_key}}:
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}}')
ch := chargehound.New("{{your_chargehound_test_key}}", nil)
params := chargehound.RetrieveDisputeParams{
ID: "{{dispute_from_step_1}}",
}
dispute, err := ch.Disputes.Retrieve(¶ms)
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.
curl https://api.chargehound.com/v1/disputes/{{dispute_from_step_1}}/submit \
-u {{your_chargehound_test_key}}:
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}}')
ch := chargehound.New("{{your_chargehound_test_key}}", nil)
params := chargehound.UpdateDisputeParams{
ID: "{{dispute_from_step_1}}",
}
_, err := ch.Disputes.Submit(¶ms)
import com.chargehound.Chargehound;
Chargehound chargehound = new Chargehound("{{your_chargehound_test_key}}");
chargehound.disputes.submit("{{dispute_from_step_1}}");
Because Chargehound creates live mode disputes with webhooks from Checkout, testing end to end requires creating a dispute in Checkout. You can do this by creating a charge with a test card that simulates a dispute. If you have a test environment, you can create a charge there to simulate a dispute end to end in your system. You can also create a charge with the Checkout API or from the Checkout 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=warning_needs_response&state=needs_response \
-u test_123
var chargehound = require('chargehound')(
'test_123'
);
async function respondToBacklog (startingAfterId=null) {
var params = {
state: ['warning_needs_response', 'needs_response']
};
// Use the dispute ID to page.
if (startingAfterId) {
params['starting_after'] = startingAfterId;
}
var res = await chargehound.Disputes.list(params);
await Promise.all(res.data.map(async function (dispute) {
// Submit the dispute.
});
if (res.has_more) {
// Recurse to address all of the open disputes.
var startingAfterId = res.data[res.data.length - 1].id;
await respondToBacklog(startingAfterId);
}
}
import chargehound
chargehound.api_key = 'test_123'
def respond_to_backlog(starting_after_id=None):
params = {
'state': ['warning_needs_response', 'needs_response']
}
# Use the dispute ID to page.
if starting_after_id:
params['starting_after'] = starting_after_id
res = chargehound.Disputes.list(**params)
for dispute in res['data']:
# Submit the dispute.
if res['has_more']:
# Recurse to address all of the open disputes.
starting_after_id = res['data'][-1]['id']
respond_to_backlog(starting_after_id)
require 'chargehound'
Chargehound.api_key = 'test_123'
def respond_to_backlog(starting_after_id)
params = {
state: %w[needs_response warning_needs_response]
}
# Use the dispute ID to page.
if starting_after_id
params['starting_after'] = starting_after_id
end
res = Chargehound::Disputes.list(params)
res['data'].each { |dispute|
# Submit the dispute.
}
if res['has_more']
# Recurse to address all of the open disputes.
starting_after_id = res['data'][-1]['id']
respond_to_backlog(starting_after_id)
end
end
import (
"github.com/chargehound/chargehound-go"
)
ch := chargehound.New("test_123", nil)
func respondToBacklog (startingAfterId string) {
params := chargehound.ListDisputesParams{
State: []string{"warning_needs_response", "needs_response"},
StartingAfter: startingAfterId
}
response, err := ch.Disputes.List(¶ms)
for _, dispute := range response.Data {
// Submit the dispute.
}
if response.HasMore == true {
// Recurse to address all of the open disputes.
nextStartingAfterId := response.Data[len(response.Data)-1].ID
respondToBacklog(nextStartingAfterId)
}
}
import com.chargehound.Chargehound;
import com.chargehound.models.DisputesList;
import com.chargehound.models.Dispute;
Chargehound chargehound = new Chargehound("test_123");
public void respondToBacklog(String startingAfterId) {
DisputesList.Params.Builder paramsBuilder = new DisputesList.Params.Builder()
.state("warning_needs_response", "needs_response");
// Use the dispute ID to page.
if (startingAfterId != null) {
paramsBuilder = paramsBuilder.startingAfter(startingAfterId);
}
DisputesList.Params params = paramsBuilder.finish();
DisputesList result = chargehound.Disputes.list(params);
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.
String nextStartingAfterId = result.data[result.data.length - 1].id;
respondToBacklog(nextStartingAfterId)
}
}
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 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
.
Static IP addresses
If you need to allowlist individual IP addresses in your firewall you can opt to have webhook calls sent from a fixed range of IP addresses on your settings page.
Webhook calls will then be sent from one of the following IP addresses if you opt to use a static IP:
3.211.115.112
3.212.160.248
3.212.186.185
34.194.118.97
34.200.90.111
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.
Test webhook
A test webhook can be triggered from your settings page when adding or editing a webhook. The test webhook is intended to help you verify your webhook URL, test webhooks will only be sent when you trigger them. The test webhook "type" will always be "test", "livemode" will match the mode of the webhook, and the ID will be randomly generated.
Example request:
{
"id": "wh_123",
"type": "test",
"object": "webhook",
"livemode": true
}
The test 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 webhook. |
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 or edited in Chargehound.
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 by Chargehound.
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 that was submitted by Chargehound 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(¶ms)
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 , unchecked . |
address_zip_check | string | optional | State of address zip check (if available). One of pass , fail , unavailable , unchecked . |
cvc_check | string | optional | State of cvc check (if available). One of pass , fail , unavailable , unchecked . |
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.) |
correspondence | array | optional | A list of communications with the customer. (See Customer correspondence for details.) |
past_payments | array | optional | History of the customer's valid, non-disputed transactions using the same card. (See Past payments 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(¶ms)
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.) |
Braintree Direct
In typical integrations with Chargehound you supply evidence and respond to disputes using the Chargehound API; however, in some cases supporting another third party integration may not be possible or desired.
With a Braintree Direct integration you supply evidence and respond to disputes with Chargehound, but you do so using the Braintree API.
There are 3 simple steps to a Braintree Direct integration:
1) When a dispute is created in Braintree, you'll handle Braintree's "Dispute Opened" webhook notification.
2) You'll collect the evidence needed by your Chargehound template(s).
3) You'll send the evidence fields to the Braintree API. You'll do this by updating the custom fields of the disputed transaction in Braintree. Chargehound will take it from there.
Setting up webhooks
You'll need to handle Braintree's "Dispute Opened" webhook notification. You can configure your Braintree webhooks in your Braintree dashboard. If you haven't defined any webhooks yet, follow the instructions for how to create a webhook here.
Setting up custom fields
You'll need to define the evidence fields used in your template(s) as Braintree custom fields. Follow the instructions for how to create custom fields here. The custom fields will need to be "Store and Pass Back" fields.
In addition to your template evidence fields, you'll need to define a few custom fields that will be used to take actions in Chargehound.
chargehound_template
This field can be used to set the template used by Chargehound. Set this field to a template ID.
chargehound_submit
Setting this field to "true"
will tell Chargehound to submit the dispute.
chargehound_queue
Setting this field to "true"
will tell Chargehound to queue the dispute for later submission. (See Queuing for submission for details.)
chargehound_force
Setting this field to "true"
will tell Chargehound to override any manual review rules. (See Manual review for details.)
chargehound_version
This field can be used to override the Chargehound API version. (See Versioning for details.)
Updating custom fields
mutation UpdateTransactionCustomFields($input: UpdateTransactionCustomFieldsInput!) {
updateTransactionCustomFields(input: $input) {
clientMutationId,
customFields {
name
value
}
}
}
{
"input": {
"transactionId": "ch_123",
"clientMutationId": "TEST EXAMPLE",
"customFields": [
{
"name": "chargehound_template",
"value": "unrecognized"
},
{
"name": "chargehound_submit",
"value": "true"
},
{
"name": "customer_name",
"value": "Susie Chargeback"
}
]
}
}
After you handle the "Dispute Opened" webhook notification, you'll gather the response evidence and update the Braintree custom fields. You'll use Braintree's GraphQL API to update the custom fields of the disputed transaction. If you encounter an error like "Custom field is invalid" you may need to create the custom field, follow the instructions for setting up custom fields here.
Braintree custom fields only support strings, but Chargehound will convert amounts or numbers from strings when needed. See the guidelines for formatting fields here.
Verifying and debugging
While with a Braintree Direct integration you only interact with the Braintree API, you'll still want to check on the disputes in Chargehound to ensure that the evidence fields you are sending are correct and that the response is submitted.
You can easily find the dispute in Chargehound using the same ID used by Braintree. By clicking "View Logs" on the dispute page you will be able to see the updates made to the dispute by the Braintree Direct integration, this can help you spot and debug any issues.
Data Requests
You can use the API to request to retrieve or remove the data in Chargehound for a particular person, identified by name or email.
Retrieve Data
Definition:
POST /v1/personal-data/retrieve
Example request:
curl https://api.chargehound.com/v1/personal-data/retrieve \
-u test_123: \
-d email="susie@example.com" \
-d name="Susie Chargeback" \
-d notification_email="your_email@example.com"
Example response:
{
"success": {
"message": "Data retrieval request submitted."
},
"url": "/v1/personal-data/retrieve",
"livemode": false
}
Retrieve the data in Chargehound for a particular person.
Parameters
Parameter | Type | Required? | Description |
---|---|---|---|
string | required | The email of the person whose data is to be retrieved. | |
name | string | required | The name of the person whose data is to be retrieved. |
notification_email | string | required | Once the data request is fulfilled, the data will be sent to this email. |
Possible errors
Error code | Description |
---|---|
400 Bad Request | Missing or invalid parameters. |
Remove Data
Definition:
POST /v1/personal-data/remove
Example request:
curl https://api.chargehound.com/v1/personal-data/remove \
-u test_123: \
-d email="susie@example.com" \
-d name="Susie Chargeback" \
-d notification_email="your_email@example.com"
Example response:
{
"success": {
"message": "Data removal request submitted."
},
"url": "/v1/personal-data/remove",
"livemode": false
}
Remove the data in Chargehound for a particular person.
Parameters
Parameter | Type | Required? | Description |
---|---|---|---|
string | required | The email of the person whose data is to be removed. | |
name | string | required | The name of the person whose data is to be removed. |
notification_email | string | optional | Once the data removal is complete, a confirmation can be sent to this email. |
Possible errors
Error code | Description |
---|---|
400 Bad Request | Missing or invalid parameters. |