Braintree API
Prepare Braintree Level 2/3 fields from a linked or inline invoice, merge them into transaction.sale or submitForSettlement, and report outcomes.
On this page
Authentication
Send a Braintree-integration Carden API key as a bearer token from a trusted server. Stripe, Square, and QuickBooks keys are rejected even if their scopes match. Every response includes x-carden-request-id; keep it for support.
Authorization: Bearer <CARDEN_API_KEY>
Content-Type: application/jsonPrepare from a QuickBooks invoice
POST /api/v1/braintree/transaction-enrichment/from-invoiceRequires invoices:read and enrichment:write. Carden reads one imported, open, fully unpaid invoice and returns Braintree fields plus immutable preparation evidence.
| Field | Type | Rule |
|---|---|---|
| invoiceId | quickbooksInvoiceId | invoiceNumber | string, exactly one | invoiceId is a Carden UUID. Use invoiceNumber only when it is unambiguous. |
| amountMinor | positive integer | Must equal the full invoice total. Partial and combined payments are not supported. |
| currency | "usd" | Lowercase. Level 2/3 fields are prepared for USD only. |
| idempotencyKey | string, ≤160 | Characters A–Z, a–z, 0–9, and . _ : -. Persist it before the first request and reuse it only for the same request. |
| expectedRevision | string, optional | Rejects the request if the invoice changed since a reviewed revision. |
Prepare a QuickBooks invoice for Braintree
curl https://www.cardenpay.com/api/v1/braintree/transaction-enrichment/from-invoice \
-H "Authorization: Bearer $CARDEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"invoiceId":"7d4c2a1e-5b8f-4c3d-9a6e-2f1b0c8d7e65","amountMinor":11300,"currency":"usd","idempotencyKey":"prepare_attempt_1042_1"}'{
"object": "braintree.transaction_enrichment",
"api_version": "v1",
"data": {
"amount": "113.00",
"orderId": "INV-1042",
"purchaseOrderNumber": "PO-2081",
"taxAmount": "8.00",
"shippingAmount": "5.00",
"discountAmount": "0.00",
"lineItems": [
{
"kind": "debit",
"name": "Industrial filter",
"productCode": "FILTER-01",
"quantity": "2",
"unitOfMeasure": "each",
"unitAmount": "50.00",
"totalAmount": "100.00",
"taxAmount": "8.00"
}
]
},
"preparation": {
"id": "0b6f3e2d-9c1a-4f7e-8d5b-3a2c1e0f9d84",
"invoiceId": "7d4c2a1e-5b8f-4c3d-9a6e-2f1b0c8d7e65",
"snapshotId": "c2e8a4f1-6d3b-4a9e-b7c5-1f0e2d3c4b5a",
"revision": "rev_1042_3",
"sourceIntegrationId": "5a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
"sourceConnectionId": "6b2c3d4e-5f6a-4b7c-9d8e-0f1a2b3c4d5e",
"checkedThrough": "2026-01-01T11:55:00.000Z",
"createdAt": "2026-01-01T12:00:00.000Z",
"amountMinor": 11300,
"currency": "usd"
}
}A 409 or 422 response means the invoice is not ready. The body is {"error": {"type", "message", "issues": [...]}}. Each issue has a code, a path, and a message; see preparation issue codes.
Prepare from an inline invoice
POST /api/v1/braintree/transaction-enrichmentRequires enrichment:write. Send a complete, authoritative CanonicalInvoice v1. The transaction amount is the invoice total.
| Field | Type | Rule |
|---|---|---|
| invoice | CanonicalInvoice v1 | Required. Line-level tax and discount are explicit on every line. |
| options.shipsFromPostalCode | string, optional | Up to 10 letters, digits, spaces, or hyphens. Your fulfillment postal code; invoices do not carry it. |
| options.purchaseOrderNumberSource | "purchase_order" | "invoice_number" | Default purchase_order uses the buyer's PO. invoice_number is your attestation that buyers use the invoice number as their reference. |
{
"invoice": {
"invoiceNumber": "INV-1042",
"purchaseOrderReference": "PO-2081",
"status": "open",
"currency": "USD",
"subtotalAmountMinor": 10000,
"taxAmountMinor": 800,
"shippingAmountMinor": 500,
"discountAmountMinor": 0,
"totalAmountMinor": 11300,
"balanceAmountMinor": 11300,
"issuedAt": null,
"dueAt": null,
"voidedAt": null,
"lineItems": [
{
"sequence": 1,
"productName": "Industrial filter",
"description": null,
"productCode": "FILTER-01",
"commodityCode": null,
"quantity": "2",
"unitOfMeasure": "each",
"unitAmountMinor": 5000,
"subtotalAmountMinor": 10000,
"taxAmountMinor": 800,
"discountAmountMinor": 0,
"totalAmountMinor": 10800
}
]
},
"options": {
"shipsFromPostalCode": "60601",
"purchaseOrderNumberSource": "purchase_order"
}
}{
"object": "braintree.transaction_enrichment",
"api_version": "v1",
"data": {
"amount": "113.00",
"orderId": "INV-1042",
"purchaseOrderNumber": "PO-2081",
"taxAmount": "8.00",
"shippingAmount": "5.00",
"discountAmount": "0.00",
"lineItems": [
{
"kind": "debit",
"name": "Industrial filter",
"productCode": "FILTER-01",
"quantity": "2",
"unitOfMeasure": "each",
"unitAmount": "50.00",
"totalAmount": "100.00",
"taxAmount": "8.00"
}
],
"shipsFromPostalCode": "60601"
}
}| Status | Meaning |
|---|---|
| 400 | The body does not match the contract, or it contains credentials or card data. |
| 422 | {"error": {"type": "validation_error", "issues": [{code, path, message}]}}. The invoice cannot produce valid Level 2/3 fields. |
Enrichment fields
Amounts are Braintree decimal strings, such as "107.50". These keys are accepted unchanged by both gateway.transaction.sale and gateway.transaction.submitForSettlement.
| Field | Rule |
|---|---|
| amount | Transaction amount; the full invoice total. |
| orderId | The invoice number. |
| purchaseOrderNumber | The buyer's PO, up to 17 characters. |
| taxAmount / shippingAmount / discountAmount | Transaction-level amounts from the invoice. |
| shipsFromPostalCode | Present only when you supplied it. |
| lineItems[].kind | Always "debit". |
| lineItems[].name | Up to 35 characters. |
| lineItems[].description | Optional, up to 127 characters. |
| lineItems[].productCode | SKU, up to 12 characters. |
| lineItems[].commodityCode | Optional, up to 12 characters. |
| lineItems[].quantity | Decimal string with at most 4 fraction digits. |
| lineItems[].unitOfMeasure | Up to 12 characters. |
| lineItems[].unitAmount | Unit price. |
| lineItems[].totalAmount | quantity × unitAmount, before tax and discount. |
| lineItems[].taxAmount | Only when the invoice has line-level tax. |
| lineItems[].discountAmount | Only when the line has a discount. |
Up to 249 line items. Carden checks that line totals plus tax and shipping minus discount equal amount.
Merge into your Braintree call
Spread data into your own gateway.transaction.sale request alongside your paymentMethodNonce. For auth-then-capture, pass data.amount as the settlement amount and the remaining fields as options to gateway.transaction.submitForSettlement. Carden never receives your nonce, card data, or gateway credentials.
- Save preparation.id with the payment attempt and send it as enrichmentRequestId when you report.
- Do not merge the preparation object into the Braintree request.
- A Carden error never justifies retrying a Braintree sale. Reconcile Braintree state first.
Report a payment outcome
POST /api/v1/braintree/payment-reportsRequires payments:write. Report each change in the attempt with cumulative amounts. Reuse eventId only for identical retries; use a new eventId for each new observation of the same attemptId.
| Field | Rule |
|---|---|
| eventId / attemptId | Required stable references, up to 160 characters. |
| transactionId | Optional Braintree transaction ID, lowercase letters and digits. |
| merchantAccountId | Optional Braintree merchant account ID. |
| status | authorized, captured, partially_refunded, refunded, failed, canceled, or unknown. |
| currency | Lowercase three-letter code. |
| amountMinor / capturedAmountMinor / refundedAmountMinor | Cumulative integer minor units. Captured cannot exceed amount; refunded cannot exceed captured. |
| occurredAt | ISO 8601 timestamp with offset. |
| enrichmentRequestId | Optional preparation.id from the enrichment response. |
| invoiceReference / errorCode / providerEventId | Optional references; errorCode is a lowercase snake_case code. |
| source | "merchant_webhook" or "api". |
| Braintree status | Carden status |
|---|---|
| authorized | authorized |
| submitted_for_settlement, settling, settlement_pending, settled | captured, or partially_refunded / refunded from cumulative refunds |
| voided | canceled |
| processor_declined, gateway_rejected, settlement_declined, failed | failed |
Report a Braintree payment outcome
curl https://www.cardenpay.com/api/v1/braintree/payment-reports \
-H "Authorization: Bearer $CARDEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"eventId":"report_attempt_1042_captured_1","attemptId":"attempt_1042_1","transactionId":"k7m2p9qx","status":"captured","currency":"usd","amountMinor":11300,"capturedAmountMinor":11300,"refundedAmountMinor":0,"occurredAt":"2026-01-01T12:01:00.000Z","enrichmentRequestId":"0b6f3e2d-9c1a-4f7e-8d5b-3a2c1e0f9d84","invoiceReference":"INV-1042","source":"api"}'{
"object": "braintree.payment_report",
"api_version": "v1",
"data": {
"id": "report_example",
"duplicate": false
}
}duplicate: true acknowledges an identical retry. A 409 means the eventId already identifies a different report, or the currency or cumulative amounts conflict with earlier reports.
Preparation issue codes
| Code | Meaning |
|---|---|
| missing_purchase_order_number | The invoice has no buyer PO. |
| purchase_order_number_too_long | The PO is longer than 17 characters. |
| purchase_order_number_invalid | The PO is not printable ASCII. |
| missing_product_code | A line has no SKU or merchant-confirmed product code. |
| missing_unit_of_measure | A line has no merchant-confirmed unit. |
| identifier_too_long | A product code, commodity code, or unit exceeds Braintree's limit. |
| identifier_invalid | An identifier is not printable ASCII. |
| invalid_quantity | A quantity is not positive or needs more than 4 fraction digits. |
| unsupported_currency | The invoice is not in USD. |
| missing_item_reference | A QuickBooks line has no item reference. |
| missing_line_tax | Line-level tax is missing on a line. |
| arithmetic_mismatch | Line, tax, shipping, discount, or total amounts do not reconcile. |
| payment_amount_mismatch | amountMinor does not equal the invoice total. |
| invoice_not_open | The invoice is not open or was voided. |
| invoice_balance_not_full | Part of the invoice is already paid. |
- Identifiers (PO number, SKU, commodity code, unit) are rejected when too long, never truncated.
- Descriptive names and descriptions are shortened to Braintree's limits.
- Carden never invents a PO number, SKU, unit, or tax amount.
- For checkout readiness, a PO, SKU, or unit gap downgrades the payment to ordinary (no Level 2/3 fields) instead of blocking it.
Braintree webhook endpoint
POST /api/integrations/braintree/webhooks/{endpointId}Braintree posts form-encoded bt_signature and bt_payload values. Carden verifies the signature with the merchant's restricted API key and records the notification once. You configure this URL in Braintree; you do not call it.
| Status | Body or code |
|---|---|
| 200 | {"received": true, "kind", "status", "duplicate"} |
| 400 | invalid_signature or invalid_event |
| 404 | endpoint_not_found |
| 409 | credentials_required or connection_unavailable |
| 410 | endpoint_disabled |