> For the complete documentation index, see [llms.txt](https://even-financial.gitbook.io/developer-center/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://even-financial.gitbook.io/developer-center/channel-partner-reporting/report-options/postback-reporting.md).

# Postback Reporting

{% hint style="success" icon="envelope-open" %}
**For questions or to request setup**

Reach out to your Partner Solutions Manager, or email <partnersupport@engine.tech>
{% endhint %}

## Overview

Postback Reporting sends near real-time webhook notifications ("postbacks") when a monetizable event occurs for a lead that originated from your traffic. A monetizable event is also known as a **funded loan**, **conversion**, or **payout event**.

* This unlocks&#x20;
  * Faster, more accurate conversion tracking and attribution
  * Deeper analytics integrations
  * Dynamic bidding
  * Real-time rewards
* Today, that means payout events: the same line items you already see in your Looker or invoice reports, sent the moment a payout is recorded in our system.
* Coming soon: Postbacks for lead events that happen before conversion, like an offer click.

## How It Works

<figure><img src="/files/GhFhgb1Mk7i8szhMnlPU" alt=""><figcaption></figcaption></figure>

1. **Consumer interacts** with your experience.
2. **You send** Engine a client tag (typically), which triggers a lead UUID.
3. **Consumer converts**, e.g. a funded loan.
4. **Engine finds out**: for CPA campaigns, the Financial Institution reports the conversion; for CPC campaigns, Engine already knows the moment it logs the click.
5. **Engine sends a postback** to the URL you've provided, as soon as the event is recorded.&#x20;
   1. If a lead converts on multiple offers, you'll get a separate postback for each one, each with its own transaction ID and payout amount.&#x20;
   2. Every postback includes the payout amount, the transaction ID, and your client tag, if provided.

> **Example:** a user clicks a CPC offer (payout: $0.50) → you get a postback. That same user also converts on a CPA offer (payout: $100) → you get a second postback.

## Setup Requirements

{% hint style="danger" icon="exclamation" %}
The named params below (`clientId`, `userId`, `yourMappedTransactionId`, etc.) are examples.

**Engine does not dictate your naming:** You tell us your preferred keys and structure for both the URL/method and the request body, and we build the postback to match.
{% endhint %}

<table data-search="false"><thead><tr><th width="136.140625">What We Need</th><th width="236.86328125">What It's Used For</th><th width="362.34375">Format &#x26; Examples</th></tr></thead><tbody><tr><td>Postback URL</td><td>The endpoint Engine sends the postback to</td><td><p>Full URL string</p><p><code>yourUrl.com/postbackreporting</code></p></td></tr><tr><td>HTTP Method</td><td>Determines whether Engine fires the postback as a GET or POST request</td><td><code>GET</code> or <code>POST</code></td></tr><tr><td>Payload structure</td><td>Tells us where to put the values: in the URL or in a JSON body</td><td><p>GET: Query or path params in the URL<br><code>?payout=100.00&#x26;clientId=abc123.</code></p><p>POST: Query/path params, or JSON <code>{"payout": "100.00", "clientId": "abc123"}</code></p></td></tr><tr><td>Payout amount field name</td><td>Maps Engine's payout dollar amount to whatever param/field name your system expects</td><td><p>By default, we return dollars with 2 decimals</p><p><code>?payout={payout.payoutInDollars}</code><br></p><p>Also supported:<br><code>payout.payoutInCents</code></p><p><code>payout.payoutInDollars</code></p></td></tr><tr><td>Transaction ID field name</td><td>Maps Engine's internal payout event ID (our "Transaction ID") to your field, so you can reference it back to us</td><td><p>This is Engine's Transaction ID, let us know if you want it called <code>transactionId</code> or if your API expects some other field in order to store it</p><p><code>?yourMappedTransactionId={engineTransactionId}</code></p></td></tr><tr><td>Your client tag key(s)</td><td>Echoes back your own attribution ID(s) - whatever you sent us on the original lead - under the key name(s) you choose</td><td><p>Any identifier(s) you provide when posting the lead that you use for attribution on your end (e.g., you send <code>subId</code>, we echo it as <code>?userId={subId}</code>).</p><p>Please provide the <strong>key</strong> of the client tag(s) desired, and we will include the value you provided for that lead for that tag.</p></td></tr><tr><td>Sample "known-good" payload</td><td>Required to confirm your endpoint returns a success response before we push the report live</td><td>A real request with values you confirm returns a 2xx, no invalid-ID errors</td></tr><tr><td><p>Engine Lead UUID</p><p><a class="button primary small">Optional</a></p></td><td>Referencing this speeds up troubleshooting if an issue is encountered</td><td><code>?partnerClientId={engineLeadUuid}</code></td></tr><tr><td>Invalidations<br><a class="button primary small">Optional</a></td><td>Do you want a follow-up notification if a payout is later reversed/deleted?</td><td><p>Yes/No (Default: No)</p><p>Same <code>transaction_id</code> as the original, possibly a negative amount.</p></td></tr></tbody></table>

## Best Practices

<table><thead><tr><th width="46.4305419921875"></th><th></th></tr></thead><tbody><tr><td>⚙️</td><td>Treat postbacks as a production data pipeline, not just a notification</td></tr><tr><td>🔁</td><td><p><strong>Idempotency</strong><br></p><ul><li>Use the Transaction ID as your unique key</li><li>If you receive the same Transaction ID twice, don't create a duplicate record</li></ul></td></tr><tr><td>🗂️</td><td><p><strong>Store Engine identifiers</strong><br></p><ul><li>Keep the Transaction ID (for deduplicating payouts) </li><li>Keep the Engine Lead UUID (for reconciling issues with our team) on every record</li></ul></td></tr><tr><td>🏷️</td><td><p><strong>Send client tags</strong> <br></p><ul><li><strong>Review</strong> <a href="/pages/d1qs6fwEzoPheaLTcAFE#supported-client-tag-keys-all-integration-types"><strong>Client Tags for Attribution</strong> </a><strong>for structuring &#x26; supported tags</strong></li><li>Include one on every lead you post to Engine (e.g. <code>clientId</code>, <code>campaignId</code>, <code>subid</code>, <code>sourceId</code>) to attribute payouts back to your own users, placements, or campaigns; we'll echo it back on the matching postback.</li><li><strong>Never send PII in a client tag</strong></li></ul></td></tr><tr><td>✅</td><td><p><strong>Return a clear success response</strong><br></p><ul><li>Acknowledge the postback quickly</li><li>If you need heavier downstream processing, queue it internally rather than doing it synchronously in the request</li></ul></td></tr><tr><td>📝</td><td><p><strong>Log everything</strong><br></p><ul><li>Keep the raw inbound request, response status, and receipt timestamp. </li><li>This makes missing payouts, duplicate deliveries, field-mapping issues, and invoice disputes much faster to investigate</li></ul></td></tr></tbody></table>

### Handling Invalidations

Invalidations happen when a previously recorded payout is later canceled or revised, for example a funded loan cancellation, duplicate or erroneous data from the Financial Institution, or a retroactive contract update.

* Invalidations are uncommon, and most partners don't opt in to receiving them today.&#x20;
* If you skip them, your internal numbers may occasionally drift from Engine's Looker/Invoice reports (which do account for invalidations), requiring manual reconciliation during monthly invoicing.&#x20;
* We recommend opting in and building support for invalidations into your ingestion logic.
* Invalidations can be sent in whatever format works for you, for example: an `event` field (`"converted"` vs `"invalidated"`), a negative payout amount for the same Transaction ID, or a `deletedAt` timestamp for the same Transaction ID.

{% tabs %}
{% tab title="Example Scenario" %}

<table><thead><tr><th width="260.95654296875" valign="middle">Scenario</th><th width="100.4305419921875" valign="middle">Payout</th><th width="124.1031494140625" valign="middle">Invalidation</th><th>New Payout (if any)</th></tr></thead><tbody><tr><td valign="middle">Funded loan canceled</td><td valign="middle">$100</td><td valign="middle">-$100</td><td>None</td></tr><tr><td valign="middle">Duplicate record sent</td><td valign="middle">$100</td><td valign="middle">-$100</td><td>None</td></tr><tr><td valign="middle">Contract updated retroactively</td><td valign="middle">$100</td><td valign="middle">-$100</td><td>$110 <br>(new Transaction ID)</td></tr></tbody></table>
{% endtab %}

{% tab title="Example POST" %}

```json
{
  "payout": "-100.00",
  "transactionId": "eadc3ae3-f817-4762-a65a-e46f0b1732b4",
  "engineLeadUuid": "e7f5b06a-25e9-11ee-be56-0242ac120002",
  "clientId": "abc123",
  "campaignId": "email",
  "event": "invalidated"
}
```

{% endtab %}

{% tab title="Example GET" %}
[https://partner.example.com/postback?payout=-100.00\&clientId=abc123\&campaignId=email\&engineLeadUuid=eadc3ae3-f817-4762-a65a-e46f0b1732b4\&event=invalidated](https://partner.example.com/postback?payout=100.00\&clientId=abc123\&campaignId=email\&engineLeadUuid=eadc3ae3-f817-4762-a65a-e46f0b1732b4\&event=invalidated)
{% endtab %}
{% endtabs %}

### Testing & Monitoring

**Before go-live**, confirm:

* [ ] Your endpoint is reachable and authentication works
* [ ] It accepts the exact payload format you requested
* [ ] Identifiers and payout values are stored correctly
* [ ] Your team knows how invalidations will be handled

💡 *Pro Tip to Speed up Launch: Provide a tested sample payload that already returns success.*<br>

**After go-live**, monitor for:

* Non-2xx responses
* Authentication failures
* Timeout errors
* Drops in expected postback volume

{% hint style="danger" %}
**If you spot an issue, contact the Engine team with the following**

* The approximate timestamp
* Transaction ID
* Engine Lead UUID when available
  {% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://even-financial.gitbook.io/developer-center/channel-partner-reporting/report-options/postback-reporting.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
