Documentation
Revenue

Connect Dodo

Connect Dodo Payments to FounderHQ.

Connect Dodo Payments so FounderHQ records every payment, refund, dispute, and subscription change. The recommended path is a read-only API key — there is no webhook to configure, and revenue appears within a few minutes.

Before you start

You need a FounderHQ brand with the Events SDK installed, and a live Dodo Payments business you can open the dashboard for.

Which should I choose?

ModeChoose it whenYour revenue appears
Paste an API keyYou want the simplest setupWithin a few minutes
WebhookYou want to configure every event yourselfIn seconds

Paste an API key

  1. In FounderHQ, open Settings → Revenue.
  2. Choose the brand, then Dodo Payments, then Paste an API key. It is marked Recommended.
  3. Open Dodo API Keys and choose Add API Key.
  4. Leave Enable write access off. This makes the key read-only.
  5. Copy the live dp_live_ key into FounderHQ and connect.

FounderHQ checks the key against Dodo's live API and binds the business it belongs to. A test-mode key is refused. The key is encrypted, never included in revenue evidence, and can be revoked from Dodo anytime.

There is no webhook setup. FounderHQ checks Dodo every few minutes and reads payments, refunds, subscriptions, and disputes. Use Sync now when you need an immediate refresh.

Webhook (manual)

Create the connection in FounderHQ

In FounderHQ, open Settings → Revenue. Choose Dodo Payments, the brand, then select Webhook.

Do not enter a business ID. Dodo puts the business on every signed event, and FounderHQ binds it from the first verified event. After that, events from any other business are rejected.

FounderHQ gives you a webhook URL:

https://app.getfounderhq.com/api/hooks/payments/dodo/CONNECTION_ID

Add the endpoint in Dodo and select the events

payment.succeeded
refund.succeeded
refund.failed
subscription.active
subscription.cancelled
subscription.expired
subscription.failed
subscription.on_hold
subscription.plan_changed
subscription.renewed
subscription.updated
dispute.accepted
dispute.cancelled
dispute.challenged
dispute.expired
dispute.lost
dispute.opened
dispute.won

FounderHQ ignores any other event type.

Paste the signing secret back

Dodo mints the signing secret, not FounderHQ. Copy it from the Dodo endpoint and paste it into the FounderHQ connection.

When you rotate the secret later, FounderHQ accepts both the old and the new secret for 24 hours. That matches Dodo's own rotation window, so no event is lost mid-rotation.

Send a test event

Send a signed test event from Dodo. FounderHQ verifies the signature, binds your business ID, and marks the connection Connected.

Attach attribution at checkout creation

Dodo carries your metadata from checkout into the payment. Build it with the Node SDK and pass it as the metadata object when you create the payment or subscription.

// app/api/dodo-checkout/route.ts
import { checkoutMetadata } from "@founderhq/events-node";

export async function POST(request: Request) {
  const { identity, productId } = await request.json();
  const metadata = checkoutMetadata({
    anonymousId: identity.fhq_anonymous_id,
    sessionId: identity.fhq_session_id,
  });

  // Pass `metadata` as the metadata field of your Dodo checkout call.
  return Response.json({ productId, metadata });
}

The browser supplies identity from founderhq.checkoutMetadata(). See checkout metadata and payment links.

Payments for an account

When Revenue owner is Organization, send the account with the checkout. Request the token on your server for the signed-in payer and the account they buy for. Add it to the same metadata:

import { checkoutMetadata } from "@founderhq/events-node";
import { events } from "./founderhq"; // your FounderHqNode client

const { contextToken } = await events.accountContextToken({
  account: workspace.id,
  userId: user.id,
  idempotencyKey: `checkout_${checkoutId}`,
});
const metadata = {
  ...checkoutMetadata({
    anonymousId: identity.fhq_anonymous_id,
    sessionId: identity.fhq_session_id,
  }),
  fhq_account_context: contextToken,
};

Dodo keeps this metadata on the payment, so the payment goes to that account. The subscription stays with it on every renewal. FounderHQ checks that the payer belongs to that account when the payment arrives. The request for a token fails only when the account is closed. Then create the checkout without the token.

Without the token, FounderHQ finds the payer by fhq_anonymous_id or by the customer email that Dodo sends. A payer in exactly one account pays for that account. A payer in several accounts, or in none, pays as a contact, and the subscription stays with that contact. A Dodo payment link has no metadata, so it always works this way. See Which account gets a payment.

Renewals carry no browser session. FounderHQ binds the Dodo customer and subscription on the first payment and resolves renewals through that binding. For account trials, send the server-issued fhq_account_context and fhq_anonymous_id at checkout creation. Keep both subscription.active and payment.succeeded enabled. If subscription metadata carries those fields, FounderHQ binds ownership at subscription start. Otherwise, the zero-total trial payment can bind it using its metadata and subscription_id, without booking money or changing subscription state. Verify the actual metadata location in a Dodo test-mode trial checkout; the recorded subscription fixture has no metadata. Both events and their retries converge on one ownership binding.

What creates revenue, and what does not

Dodo observationEffect in FounderHQ
Successful paymentCreates revenue. This covers first payments, renewals, and Indian recurring debits.
Successful refundSubtracts revenue.
Subscription changeUpdates subscription state and MRR only. It never creates revenue.
Dispute lost or acceptedSubtracts the disputed amount from revenue, when FounderHQ knows the original payment.
Other dispute changesStored as evidence. They do not change revenue.

Dodo fires a renewal event alongside the first payment. That is why lifecycle events never create money on their own. The API-key path follows the same rule when it reconstructs the current resource state.

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

Verify

API key

  1. The connection shows Connected as soon as Dodo verifies the key.
  2. After the first sync, open Analytics → Events & Visits and set Type to Revenue. Your payments from the last 30 days are there.
  3. In FounderHQ, open Analytics → People and open the buyer. The payment is on their Revenue tab.
  4. Open Analytics → Overview. In the Channel card, ranked by Revenue, the payment counts for the channel that brought the buyer. Switch between Closed by and Introduced by to see the last visit before the payment, or the first.

Webhook

  1. Send a signed test event from Dodo. The connection shows Connected.
  2. Take a payment through your checkout.
  3. In FounderHQ, open Analytics → People and open the buyer. The payment is on their Revenue tab.
  4. Open Analytics → Overview. In the Channel card, ranked by Revenue, the payment counts for the channel that brought the buyer. Switch between Closed by and Introduced by to see the last visit before the payment, or the first.

Troubleshoot

FounderHQ asks for a live-mode API key

The pasted key belongs to Dodo test mode. Open the live Dodo dashboard and create a read-only key there.

FounderHQ says the key belongs to another Dodo business

The connection is already bound to another business. Use a key for that business, or create a separate connection for the other one.

Dodo shows "Invalid signature"

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

The webhook connection stays on "Waiting for test"

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

AI agent or LLM? Read this page as markdown

On this page