# Dodo end to end (https://www.getfounderhq.com/docs/analytics/recipes/dodo-end-to-end)

Go from no Dodo connection to a payment that shows the channel that
brought the buyer. About 15 minutes, once.

<Callout title="Before you start">
You need a FounderHQ brand with the [web SDK](/analytics/sdks/web) installed on
your pricing page, and a Dodo Payments account you can open the dashboard
for. New to the SDK? Start with
[Quickstart: Web (snippet)](/analytics/getting-started/quickstart-web-snippet).
</Callout>

Revenue attribution needs two halves, and one half alone tells you nothing:

1. **The Dodo connection** tells FounderHQ that money moved.
2. **The checkout metadata** tells FounderHQ who to thank.

Do them in that order. A read-only API key is the recommended connection: no
webhook setup, and revenue appears within a few minutes.

## 1. Connect Dodo

Follow [Connect Dodo](/analytics/revenue/connect-dodo). In short, you:

- Create a live API key in Dodo and leave **Enable write access** off.
- Paste that key into the Dodo connection for your FounderHQ brand.
- Wait a few minutes for the first sync, or choose **Sync now**.

FounderHQ verifies the key against Dodo's live API and binds its business
before saving it. The key is encrypted and can be revoked from Dodo anytime.

Prefer webhooks? The same connection page keeps the manual webhook path. Add
the URL FounderHQ gives you, select the listed events, paste Dodo's signing
secret, and send a signed test event. Both paths feed the same revenue records.

## 2. Attach attribution when you create the checkout

Dodo carries your metadata from the checkout into the payment. The browser
holds the two IDs, and your server builds the metadata object.

Read the IDs in the browser, and post them with the rest of the checkout
request:

```ts
import { founderhq } from "@founderhq/events";

const identity = founderhq.checkoutMetadata();
// { fhq_anonymous_id, fhq_session_id }
```

On your server, build the metadata with the Node SDK and pass it as the
`metadata` object of your Dodo payment or subscription call. The server
code is on [Connect Dodo](/analytics/revenue/connect-dodo), step 5.

`checkoutMetadata()` in the browser returns an empty object when the
visitor has not granted consent. Treat that as normal, and complete the
sale without attribution.

## 3. Know what creates revenue

| Dodo event | Effect in FounderHQ |
| --- | --- |
| `payment.succeeded` | Creates revenue. This covers first payments, renewals, and Indian recurring debits. |
| `refund.succeeded` | Subtracts revenue. |
| `subscription.*` | Updates subscription state and MRR only. It never creates revenue. |
| `dispute.*` | Stored as evidence. It does not change revenue today. |

Dodo fires a renewal event alongside the first payment. That is why
lifecycle events never create money on their own.

Renewals carry no browser session. FounderHQ binds the Dodo customer and
subscription on the first payment, and resolves renewals through that
binding.

Selling in India? FounderHQ stores both the charged total and the
settlement amount. Channel revenue uses the charged total.

## Verify

1. In FounderHQ, open Revenue. The Dodo connection shows **Connected**.
2. Start a checkout on your pricing page, and take a live payment.
3. In Dodo, open the payment. Its metadata shows `fhq_anonymous_id` and
   `fhq_session_id`.
4. In FounderHQ, open Revenue. The payment shows the channel that brought
   the buyer, under **Introduced by** and **Closed by**.

## Troubleshoot

### The payment arrives, but with no channel

The webhook half works and the metadata half does not. Open the payment in
Dodo and look for the two IDs. Empty IDs mean the visitor denied consent,
or the buyer never loaded a page that runs the SDK.

### FounderHQ asks for a live-mode API key

The pasted key belongs to Dodo test mode. Create the key from your live Dodo
dashboard and leave write access off.

### The webhook connection stays on "Waiting for test"

No signed event has arrived yet. Send a test event from the Dodo endpoint.

### Dodo shows "Invalid signature"

The pasted secret is wrong, or you rotated it more than 24 hours ago. Copy
the current secret from Dodo and paste it again.
