# Paypal Braintree

Omnata's PayPal Braintree plugin replicates transaction, subscription, dispute, and customer data from your Braintree merchant account into Snowflake, with both full-refresh and incremental sync strategies.

### Prerequisites

To connect Braintree to Omnata, you need:

* An active Braintree merchant account (Sandbox or Production)
* A set of API credentials — Merchant ID, Public Key, and Private Key — generated from the Braintree control panel

Follow the [Braintree control panel docs](https://developer.paypal.com/braintree/articles/control-panel/important-gateway-credentials#api-keys) to create a new key set.

### Authentication

#### API Keys

You connect Omnata to Braintree using your Merchant ID together with a public/private API key pair. The private key is treated as a Snowflake secret.

The following fields are required to create a connection in the Omnata UI:

<table><thead><tr><th>Field</th><th>Required</th><th>Description</th><th data-hidden>Default</th></tr></thead><tbody><tr><td>Merchant ID</td><td>Yes</td><td>Your Braintree Merchant ID.</td><td>—</td></tr><tr><td>Public Key</td><td>Yes</td><td>Your Braintree Public Key.</td><td>—</td></tr><tr><td>Private Key</td><td>Yes</td><td>Your Braintree Private Key. Stored as a Snowflake secret.</td><td>—</td></tr><tr><td>Environment</td><td>Yes</td><td>Either <code>sandbox</code> (for testing) or <code>production</code> (for live transactions).</td><td>—</td></tr></tbody></table>

### Inbound Syncs

Objects are fetched incrementally wherever a usable cursor is available; otherwise the plugin falls back to full refresh.

The plugin exposes the following streams:

<table><thead><tr><th>Stream</th><th>Primary Key</th><th>Cursor Field</th><th>Sync Strategies</th><th data-hidden>API</th></tr></thead><tbody><tr><td><code>transactions</code></td><td><code>id</code></td><td><code>created_at</code></td><td>Full Refresh, Incremental</td><td>REST <code>advanced_search</code> (two-phase)</td></tr><tr><td><code>subscriptions</code></td><td><code>id</code></td><td><code>created_at</code></td><td>Full Refresh, Incremental</td><td>REST <code>advanced_search</code> (two-phase)</td></tr><tr><td><code>customers</code></td><td><code>id</code></td><td><code>created_at</code></td><td>Full Refresh, Incremental</td><td>REST <code>advanced_search</code> (two-phase)</td></tr><tr><td><code>disputes</code></td><td><code>id</code></td><td><code>receivedDate</code></td><td>Full Refresh, Incremental</td><td>GraphQL (Relay cursor pagination)</td></tr></tbody></table>

For the underlying object definitions and field reference, see the [Braintree object reference](https://developer.paypal.com/braintree/docs/reference/overview/).

#### Incremental sync — important caveats

Braintree's APIs constrain what "incremental" can capture. These caveats are inherent to the Braintree platform, not plugin design choices.

**1. Updates to existing records are not captured**

For `transactions`, `subscriptions`, and `customers`, the plugin queries Braintree's REST API with a `created_at` date range. Because `created_at` is immutable, an incremental run only returns records that were **created** since the last cursor. As such, updates to existing records are not returned

Although the records have an `updated_at` field, Braintree's search API does not support querying by `updated_at`, so the plugin cannot use it as a cursor.

{% hint style="info" icon="lightbulb-exclamation-on" %}
**Recommendation:** Pair your incremental sync with a periodic **Full Refresh + Replace** (e.g. nightly or weekly, depending on tolerance) to pick up status changes. Filter on the latest snapshot downstream.
{% endhint %}

**2. Disputes use a date-only cursor**

The `disputes` stream cursor is `receivedDate`, which Braintree exposes at day precision only. The plugin filters with `receivedDate >= since.date()`, which means each incremental run re-fetches the entire current day to avoid missing late-arriving records.&#x20;

{% hint style="info" icon="lightbulb-exclamation-on" %}
**Recommendation:** Same as above, pair incremental with a periodic Full Refresh if dispute outcome tracking matters to you.
{% endhint %}

### How the plugin handles deletes

Braintree's data model is largely **append-only or state-based**, so most "deletes" are actually status transitions rather than physical removals:

| Object        | Lifecycle                                                                                                                                                                                                      |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Transactions  | Immutable once created. Refunds, voids and chargebacks appear as new transactions or as status changes (`voided`, `submitted_for_settlement`, `settled`, etc.). Transactions are not hard-deleted via the API. |
| Subscriptions | Cancellation is a status change (`canceled`), not a deletion. The subscription record remains in the API and continues to be returned.                                                                         |
| Disputes      | Records of historical events. Cannot be deleted.                                                                                                                                                               |
| Customers     | Can be hard-deleted by an admin via the API. The customer no longer appears in `Customer.search` results after deletion.                                                                                       |

#### Recommendations

| Pattern                                             | When to use                                                                                                                                                                                                     |
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Filter on status downstream**                     | For transactions, subscriptions and disputes, treat the status field as the lifecycle signal rather than expecting deletions. A cancelled subscription is still in the data — filter it out at the model level. |
| **Periodic Full Refresh + Replace for `customers`** | The only way to remove hard-deleted customers from Snowflake. Schedule alongside your incremental runs at a lower frequency (e.g. weekly) to reconcile.                                                         |

### Managing performance

The PayPal Braintree plugin exposes the following tuning parameters under the **Tuning** section of the inbound sync configuration. These can be changed without triggering change management workflows.

| Parameter              | Default | Description                                                                                                                      |
| ---------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Parallel fetch workers | 1       | Number of parallel API workers per stream. Increase to speed up large syncs at the cost of UDF memory.                           |
| Records per upload     | 500     | Records buffered before each upload to Snowflake. Lower values reduce peak UDF memory; higher values reduce per-upload overhead. |

#### Network addresses

The plugin requires outbound access to Braintree's API endpoints from Snowflake:

* `api.braintreegateway.com` — production API
* `api.sandbox.braintreegateway.com` — sandbox API
* `payments.braintree-api.com` — production payments
* `payments.sandbox.braintree-api.com` — sandbox payments

### Outbound Syncs

Outbound syncs to Braintree are not currently supported. [Contact us](https://omnata.com/contact-us) if this is a use case you'd like to discuss.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.omnata.com/omnata-product-documentation/omnata-sync-for-snowflake/apps/paypal-braintree.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
