Skip to main content

This page is limited to a select group of individuals or a specific audience and is not yet available to the general public. Please get in touch with the product team before sharing this link.

At-Most-Once Processing

At-Most-Once Processing or Idempotency processing helps merchants ensure that duplicate API requests do not result in duplicate actions such as charging or refunding a payment multiple times. In contrast to transaction duplicate checking, this feature requires merchants to specify a unique request key, and the two features are mutually exclusive.

There are two primary categories of idempotency features:

  • Network: a given request returns the same response, including network errors
  • Logical Intent: a given request will run without causing incompatible, duplicative actions

Instead of network-level idempotency, Braintree has chosen to implement logical intent idempotency or, to avoid confusion, At-Most-Once Processing. This feature ensures that duplicate API requests do not result in duplicate actions, such as charging or refunding a payment multiple times. Duplicate requests do not reach payment networks and avoid overcharging merchants. Each identical request within a 30-day window will return the current state of that request's intent. This avoids unintended duplication and can safely retry requests when unexpected errors occur, for example, networking failures.

Details

At-Most-Once processing requires the merchant to provide an ApiRequestKey with each request to identify the logical action being attempted uniquely. Identical ApiRequestKey values within a 30-day window are considered duplicate requests.

When duplicate requests are encountered, the following are possible outcomes:

  • If other request details, such as amount, customer, or payment method, are the same as the original request, and the original request is still in progress, the gateway returns a retryable validation error.
  • If other request details, such as amount, customer, or payment method, are the same as the original request, and the original request has been completed, the current state of that action's result is returned. For example, a Charge request will return a transaction record, but the state may now be Settled instead of Settling (as well as error states such as Declined).
  • If other request details, such as amount, customer, or payment method, are not the same as the original request, the gateway returns a validation error.

Supported Actions

Currently, we support following actions

  • Authorize
  • Charge
  • Submit For Partial Capture
  • Submit For Settlement
  • Credit
  • Refund

Authorize

* authorizePaymentMethod
* authorizePayPalAccount
* authorizeVenmoAccount
* authorizeCreditCard
* authorizeInStoreCreditCard
Authorization (2-step) Scenarios (Excludes all capture scenarios)
ScenarioRequest KeyRequest ParamsTransaction stored w/ 1st request?Time since original requestLegacy API HTTP codeOutcome and response formatNotes
1NewOriginalN/AN/A200 or 201New Transaction object
2DupeUnchangedYesConcurrent422Validation Error 915233
3DupeUnchangedYes< 30 days200 or 201Original Transaction object in current state (that state may not be successful)
4*DupeUnchangedNo< 30 days422Validation Error 915234Generate new ApiRequestKey; see footnote
5**DupeChangedMaybe< 30 days422Validation Error 915232See footnote
6DupeUnchangedN/A> 30 days200 or 201New Transaction object

*Footnote for use case (4): Unlike capture scenarios (where a transaction record is already present), validation error 915234 on an auth can only be returned when no transaction record was stored. Because transaction records are durably persisted prior to attempting authorization with payment networks, the absence of a transaction record means reattempting the request cannot result in multiple authorizations.
*Footnote for use case (5): When receiving a validation error code 915232, it is the merchant’s responsibility to determine if generating a new ApiRequestKey and sending changed request parameters would result in a functional duplicate charge. Specifically, the merchant should understand why the parameters have changed. A few overlapping scenarios are possible:

  1. The original request
  • Could have been successful, resulting in an authorization.
  • Could have been unsuccessful, with no pending authorization.
  1. The changed parameters
  • Could be correct, with the ApiRequestKey being erroneously duplicative. For example, if the merchant has a bug in generating ApiRequestKey values
  • Could be incorrect, with the ApiRequestKey being correct. For example, if the merchant had a bug that incorrectly mutated the parameters If a transaction record was created (whether or not the authorization was successful) the error response will contain an additional field with that transaction’s public identifier.

Charge

* chargePaymentMethod
* chargeUsBankAccount
* chargePayPalAccount
* chargeVenmoAccount
* chargeCreditCard
* chargeInStoreCreditCard
Charge (1-step) Scenarios
ScenarioRequest KeyRequest ParamsTransaction stored w/ 1st request?Time since original requestLegacy API HTTP codeOutcome and response formatNotes
1NewOriginalN/AN/A200 or 201New Transaction object
2DupeUnchangedYesConcurrent422Validation Error 915233
3DupeUnchangedYes< 30 days200 or 201Original Transaction object in current state (that state may not be successful)
4*DupeUnchangedNo< 30 days422Validation Error 915234Generate new ApiRequestKey; see footnote
5**DupeChangedMaybe< 30 days422Validation Error 915232See footnote
6DupeUnchangedN/A> 30 days200 or 201New Transaction object

*Footnote for use case (4) Multiple scenarios may lead to this outcome. If no transaction record was created, then retrying with a new ApiRequestKey may be safely attempted. If a transaction record was created the error response will contain an additional key with the transaction's public identifier, and further investigation (and potentially intervention by Braintree) is required.

*Footnote for use case (5) Similarly to the footnote for the use case (5) for authorization scenarios, the merchant must understand why their integration has either reused an ApiRequestKey or generated different request parameters. The error response will contain an additional key with the transaction's public identifier from the original request.

Refund

* refundTransaction
ScenarioRequest KeyRequest ParamsTransaction stored w/ 1st request?Time since original requestLegacy API HTTP codeOutcome and response formatNotes
1NewOriginalN/AN/A200 or 201New Transaction object
2DupeUnchangedYesConcurrent422Validation Error 915233
3DupeUnchangedYes<30 days200 or 201Original Transaction object in current state (that state may not be successful)
4*DupeUnchangedNo<30 days422Validation Error 915234Generate new ApiRequestKey; see footnote
5**DupeChangedMaybe<30 days422Validation Error 915232See footnote
6DupeUnchangedN/A>30 daysundefined: depends on processorundefined: depends on processor

*Footnote for use case (4) Multiple scenarios may lead to this outcome. If the original transaction doesn't have a newly associated refund record retrying with a new ApiRequestKey may be safely attempted. Otherwise, further investigation (and potentially intervention by Braintree) is required.

*Footnote for use case (5) Similarly to the footnote for the use case (5) for authorization scenarios, the merchant must understand why their integration has either reused an ApiRequestKey or generated different request parameters. The error response will contain an additional key with the transaction's public identifier from the original request.

Submit For Settlement

* captureTransaction
Capture Scenarios (Excludes all authorization scenarios)
ScenarioRequest KeyRequest ParamsTime since original capture requestInitial Capture Request SucceededLegacy API HTTP codeOutcome and response formatNotes
1NewOriginalN/AN/A200 or 201Updated Transaction object
2DupeUnchangedConcurrentN/A422Validation Error 915233
3DupeUnchanged< 30 daysYes200 or 201Transaction object in the current state
4*DupeUnchanged< 30 daysNo422Validation Error 915234See footnote
5**DupeChanged< 30 daysMaybe422Validation Error 915232See footnote
6DupeUnchanged> 30 daysN/A200 or 201Updated Transaction object

*Footnote for use case (4) Multiple scenarios may lead to this outcome. If the transaction status is still Authorized, retrying with a new ApiRequestKey may be safely attempted. Otherwise, further investigation (and potentially intervention by Braintree) is required.

*Footnote for use case (5) Similarly to the footnote for the use case (5) for authorization scenarios, the merchant must understand why their integration has either reused an ApiRequestKey or generated different request parameters. The error response will contain an additional key with the transaction's public identifier from the original request.

Submit For Partial Settlement

* partialCaptureTransaction
ScenarioRequest KeyRequest ParamsPartial capture transaction stored w/ 1st request?Time since original requestLegacy API HTTP codeOutcome and response formatNotes
1NewOriginalN/AN/A200 or 201Partial capture transaction object
2DupeUnchangedYesConcurrent422Validation Error 915233
3DupeUnchangedYes< 30 days200 or 201Partial capture transaction object in current state (that state may not be successful)
4*DupeUnchangedNo< 30 days422Validation Error 915234Generate new ApiRequestKey; see footnote
5**DupeChangedMaybe< 30 days422Validation Error 915232See footnote
6DupeUnchangedN/A> 30 days200 or 201Partial capture transaction object

*_Footnote for use case (4) Multiple scenarios may lead to this outcome. Because this request stores a new partial capture transaction record before attempting capture with payment networks, the absence of a partial capture transaction record means that it is safe to retry the request.

*Footnote for use case (5) Similarly to the footnote for the use case (5) for authorization scenarios, the merchant must understand why their integration has either reused an ApiRequestKey or generated different request parameters. If a partial capture transaction record was created by an earlier request the error response will contain an additional key with the partial capture transaction's public identifier.

Credit

* refundCreditCard
* refundInStoreCreditCard
* refundUsBankAccount
ScenarioRequest KeyRequest ParamsRefund stored w/ 1st request?Time since original requestLegacy API HTTP codeOutcome and response formatNotes
1NewOriginalN/AN/A200 or 201New Refund transaction object
2DupeUnchangedYesConcurrent422Validation Error 915233
3DupeUnchangedYes< 30 days200 or 201Original Refund transaction object in current state (that state may not be successful)
4*DupeUnchangedNo< 30 days422Validation Error 915234Generate new ApiRequestKey; see footnote
5**DupeChangedMaybe< 30 days422Validation Error 915232See footnote
6DupeUnchangedN/A> 30 days200 or 201New Refund transaction object

*_Footnote for use case (4) Multiple scenarios may lead to this outcome. Because this request stores a new refund transaction record before attempting refund with payment networks, the absence of a refund transaction record means that it is safe to retry the request.

*Footnote for use case (5) Similarly to the footnote for the use case (5) for authorization scenarios, the merchant must understand why their integration has either reused an ApiRequestKey or generated different request parameters. If a refund transaction record was created by an earlier request the error response will contain an additional key with the refund transaction's public identifier.

Error Codes

Error CodeError Message
915232An ApiRequestKey may only be reused with parameters identical to the original request.
915233A previous request with this ApiRequestKey is still in-flight.
915234A previous request with this ApiRequestKey failed and cannot be retried.