> ## Documentation Index
> Fetch the complete documentation index at: https://docs.partnero.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Transaction-based attribution

> Attribute commissions to different partners per transaction using payment metadata.

By default, Partnero uses **customer-based attribution** — a customer is permanently linked to one partner, and all their transactions generate commissions for that partner.

**Transaction-based attribution** changes this. Instead of looking at who the customer belongs to, Partnero looks for a `partner_key` in the payment metadata to decide which partner should receive the commission. This means the same customer can generate commissions for different partners on different transactions.

## When to use this

* A marketplace where the same buyer purchases from different sellers, each represented by a different partner
* A SaaS platform where customers can be upsold by different agencies, and each agency should earn commission on the deals they close
* A business where the referring partner is tracked at the payment level (e.g., stored in Stripe charge metadata) rather than at the customer level

## Setting up

1. Go to **Program Settings → Edit program**
2. Find **Commission attribution model**
3. Select **Transaction-based**
4. Save

<Note>
  This setting only affects how new incoming transactions are attributed. Existing customers and commissions are not modified.
</Note>

## How it works

When a payment webhook arrives (or an API call is made), Partnero checks for a `partner_key` field in the payment metadata **before** any other customer lookup.

1. Read `partner_key` from payment metadata
2. Find the partner matching that key
3. Find or create a customer record linked to that specific partner
4. Calculate commission for that partner

If no `partner_key` is found, Partnero falls back to the standard customer-based attribution logic.

### Customer records

To support the same real-world customer being linked to multiple partners, Partnero creates separate customer records with a composite identifier:

```
{customer_id}__pkey_{partner_identifier}
```

For example, Stripe customer `cus_123` attributed to partner `PARTNER_A` becomes `stripe_cus_123__pkey_PARTNER_A`. This happens automatically — no action required on your part.

## Examples

Imagine Stripe customer `cus_123` making three separate purchases over time.

### Partner key is present

| Charge        | `partner_key` in metadata | Result                                                                     |
| ------------- | ------------------------- | -------------------------------------------------------------------------- |
| First charge  | `PARTNER_A`               | New customer created, linked to Partner A. Commission → **Partner A**      |
| Second charge | `PARTNER_B`               | Separate customer record created for Partner B. Commission → **Partner B** |
| Third charge  | `PARTNER_A`               | Existing customer record found. Commission → **Partner A** (no new record) |

Each partner gets commission only for the transactions that carry their key. The same Stripe customer generated commissions for two different partners.

### No partner key or invalid key

| Charge                  | `partner_key` in metadata | Result                                                                                                                               |
| ----------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| Charge with no key      | *(empty)*                 | Falls back to standard customer-based attribution. If the customer is already linked to a partner, that partner gets the commission. |
| Charge with invalid key | `INVALID_KEY`             | Partner not found. Falls back to standard customer-based attribution.                                                                |

<Tip>
  Transaction-based attribution is additive — it only changes behavior when a valid `partner_key` is present. All existing attribution methods (referral links, promotion codes, customer matching) continue to work as a fallback.
</Tip>

## Integration guides

### Stripe

Add `partner_key` to the metadata of charges, subscriptions, invoices, or the Stripe customer object. Partnero checks all of these locations.

**Checkout Session:**

```javascript theme={null}
const session = await stripe.checkout.sessions.create({
  line_items: [{ price: 'price_xxx', quantity: 1 }],
  mode: 'payment',
  success_url: 'https://yoursite.com/success',
  cancel_url: 'https://yoursite.com/cancel',
  metadata: {
    partner_key: 'PARTNER_A'
  }
});
```

**Subscription:**

```javascript theme={null}
const subscription = await stripe.subscriptions.create({
  customer: 'cus_123',
  items: [{ price: 'price_xxx' }],
  metadata: {
    partner_key: 'PARTNER_A'
  }
});
```

**One-time charge:**

```javascript theme={null}
const charge = await stripe.charges.create({
  amount: 9999,
  currency: 'usd',
  customer: 'cus_123',
  metadata: {
    partner_key: 'PARTNER_A'
  }
});
```

**Stripe customer metadata** (applies to all future charges for this customer unless overridden per charge):

```javascript theme={null}
await stripe.customers.update('cus_123', {
  metadata: {
    partner_key: 'PARTNER_A'
  }
});
```

<Tip>
  Partnero checks metadata in this order: charge/invoice metadata → subscription metadata → Stripe customer metadata. A `partner_key` on a specific charge overrides one set on the customer.
</Tip>

### Paddle

Add `partner_key` to the `custom_data` object on transactions or subscriptions.

```javascript theme={null}
Paddle.Checkout.open({
  items: [{ priceId: 'pri_xxx', quantity: 1 }],
  customData: {
    partner_key: 'PARTNER_A'
  }
});
```

Or server-side:

```bash theme={null}
curl https://api.paddle.com/transactions \
  -H "Authorization: Bearer YOUR_PADDLE_KEY" \
  -d '{
    "items": [{"price_id": "pri_xxx", "quantity": 1}],
    "custom_data": {
      "partner_key": "PARTNER_A"
    }
  }'
```

### WooCommerce

Add `partner_key` to the order meta data. This can be done via a plugin, custom code, or the WooCommerce REST API.

```bash theme={null}
curl https://yourstore.com/wp-json/wc/v3/orders \
  -u consumer_key:consumer_secret \
  -d '{
    "meta_data": [
      {
        "key": "partner_key",
        "value": "PARTNER_A"
      }
    ]
  }'
```

### Chargebee

Partnero reads the partner key from the Chargebee customer custom fields `cf_partnero_partner` or `cf_referral_code`. Set these fields on the customer in Chargebee, and Partnero will use them for per-transaction attribution when transaction-based mode is enabled.

### API

When using the Partnero API directly, include the `partner` field in your transaction request. The customer is automatically created and linked to that partner.

```bash theme={null}
curl https://api.partnero.com/v1/transactions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "partner": {
      "key": "PARTNER_A"
    },
    "customer": {
      "key": "customer_123",
      "email": "customer@example.com"
    },
    "key": "invoice_789",
    "amount": 99.99,
    "action": "sale"
  }'
```

<Note>
  With transaction-based attribution via the API, you don't need to create the customer separately — it's created automatically when the transaction is recorded.
</Note>

## Where `partner_key` is read from

| Integration     | Metadata location                                                          |
| --------------- | -------------------------------------------------------------------------- |
| **Stripe**      | Charge metadata → invoice/subscription metadata → Stripe customer metadata |
| **Paddle**      | Transaction or subscription `custom_data`                                  |
| **WooCommerce** | Order `meta_data`                                                          |
| **Chargebee**   | Customer fields `cf_partnero_partner` or `cf_referral_code`                |
| **API**         | `partner` field in request body                                            |

## Next steps

<CardGroup cols={2}>
  <Card title="Stripe integration" icon="stripe" href="/integrations/stripe">
    Full Stripe setup guide
  </Card>

  <Card title="Paddle integration" icon="credit-card" href="/integrations/paddle">
    Full Paddle setup guide
  </Card>

  <Card title="API reference" icon="code" href="/api-reference/transactions/create">
    Transaction API documentation
  </Card>

  <Card title="Affiliate API" icon="link" href="/guides/tracking/affiliate-api">
    Standard API integration guide
  </Card>
</CardGroup>
