Skip to main content

What this lets you do

Connect Stripe to Traceten and see which AI sources drive paying customers, not just pageviews. When a Stripe charge succeeds, Traceten matches it back to the originating session and attributes the revenue to the correct AI source.

Before you start

  • A Traceten account with at least one site tracked.
  • A Stripe account with live mode access, and permission to create restricted API keys on it.
  • The Traceten snippet installed and verified on the site where Stripe Checkout runs.
  • Workspace admin in Traceten. Connecting and disconnecting a revenue source affects the whole workspace, so only admins can do it.

Connect Stripe

You connect Stripe by pasting a restricted API key. There is no OAuth redirect, and there is no webhook to configure: Traceten creates the webhook endpoint on your Stripe account for you.
1

Create a restricted key in Stripe

Open Stripe → Developers → API keys and click Create restricted key.Give it a name (for example, Traceten) and set these permissions:Leave everything else set to None. Webhook Endpoints needs Write because Traceten creates the endpoint for you. Everything else is read-only.Payment Intents and Balance Transactions are not used on every payment, only when a charge comes in in a currency Traceten cannot price directly (for example, an ECB-unlisted currency). In that rare case Traceten reads the charge’s Balance Transaction, which carries the amount already converted into your Stripe account’s own settlement currency, so the payment is still recorded instead of being dropped. Without these two permissions that fallback simply does not run for you and such a charge is skipped, not attributed incorrectly.Click Create key, then copy it. It starts with rk_live_.
2

Paste it into Traceten

Go to app.traceten.com/dashboard, open Settings → Integrations, and choose the site your checkout runs from at the top of the page.Pick Stripe from the list of payment providers, then paste the restricted key.Click Connect.
3

Confirm the connection

Stripe shows as Connected, with your account name, your Stripe default currency, and the last four characters of the key.Traceten stores the key encrypted. After this point it is never shown again, in the dashboard or in the API, and only those last four characters are readable back.

What Traceten does with the key

In the same request, Traceten:
  1. Reads your account name, account ID and default currency from Stripe.
  2. Reads one charge and one Checkout Session, to prove the key can actually see the revenue it is being connected for. Both are read, because the import uses both.
  3. Creates a webhook endpoint on your Stripe account pointed at Traceten, subscribed to exactly seven events.
  4. Stores the key and the webhook signing secret, both encrypted.
  5. Starts importing your last 90 days of completed Checkout Sessions and paid invoices.
You can see the endpoint Traceten created under Stripe → Developers → Webhooks. These are the seven events: Subscription revenue arrives on invoice.payment_succeeded. Stripe bills a renewal as an invoice, so every renewal is attributed to the AI source that won the customer, and it carries the subscription id into the per-subscription lifetime value report. The two customer.subscription.* events are on the endpoint, but no revenue, conversion or lifetime-value figure is computed from them. charge.succeeded is on the endpoint and is deliberately not turned into revenue. A charge behind a Checkout Session or an invoice is already counted by one of the two events above, and counting the charge as well would double that order.

Refunds and net revenue

Your attributed revenue is net of refunds. When Stripe refunds a charge, Traceten subtracts that amount from the AI source the original payment was attributed to.
  • The refund is dated on the day it was issued, not the day of the original charge. A past day’s revenue figure never changes after the fact, and a day on which you refunded more than you took in reads as a negative number.
  • Conversion counts stay gross. A refunded charge still counts as a conversion. The money is reported separately as a refunded figure beside the net one.
  • Partial refunds subtract exactly what Stripe returned. A Stripe refund states its own amount rather than a running total, so two partial refunds on one charge subtract each amount once.
  • A refund for a charge Traceten never attributed leaves your totals alone. A charge from before you connected Stripe, or one outside the 90-day import, added no revenue in the first place, so there is nothing to take back off. Traceten holds such a refund and retries it rather than dropping it, in case the charge arrives later.
  • A cancelled subscription is not a refund. Cancelling stops future charges. The charges already paid were earned and stay in your totals, so cancelling changes no figure in Traceten. Only a refund takes money back off.

If the key is missing a permission

Traceten checks the key against Stripe before storing anything, and the error names the exact permission Stripe says is missing. Four permissions are proved at connect: Account read, Charges read, Checkout Sessions read, and Webhook Endpoints write. A key that can create the endpoint but cannot read charges or Checkout Sessions is refused too, rather than connecting and then failing the import. The most common cause is leaving Webhook Endpoints on Read or None. Traceten will not fall back to a slower method: without that permission there is no webhook, and without a webhook there is no revenue attribution. Payment Intents and Balance Transactions read are not checked at connect time. They are only exercised the first time a charge arrives in a currency Traceten cannot price directly. If they are missing, that one charge is skipped rather than attributed with a wrong or missing amount; add both permissions and replace the key if you see this. Nothing is saved and no endpoint is created, so you can add the permission in Stripe and paste the key again. That holds whether this is a first connect or a replacement: when you are replacing a key, Traceten creates the new endpoint and stores it before removing the old one, so a refused replacement leaves your existing connection exactly as it was.

Keys Traceten will not accept

  • Secret keys (the ones starting sk_) are rejected. A secret key can do anything in your Stripe account, including moving money. Traceten does not need that and will not hold it.
  • Publishable keys (starting pk_) are rejected. They cannot read the data Traceten needs.
  • Test-mode restricted keys (rk_test_) are rejected in production. Test-mode charges carry synthetic amounts that would corrupt your revenue totals.

Connecting Stripe with multiple sites

Stripe connects to one site, and each site connects its own Stripe account. If you track multiple sites under one account, connect Stripe once per site, choosing the site first.
1

Open the site you want to attribute

In the dashboard, open Settings → Integrations and choose the site whose revenue you want to attribute.
2

Connect that site's Stripe account

Paste a restricted key from the Stripe account that bills that site’s customers. Traceten pins the connection to the site you started from.
3

Repeat for each site

Do the same for every other site, each with the Stripe account that charges its customers.
Two rules follow from this:
  • A Stripe account can be connected to only one site at a time. Reconnecting a Stripe account that is already linked to another site moves the connection to the new site and removes revenue attribution from the first one, with no error, so long as the new site has no Stripe connection of its own. If it already has one, Traceten refuses rather than replacing it; disconnect that one first. If several sites all charge through one Stripe account, only one site can hold that connection at a time; split billing into separate Stripe accounts if each site needs its own revenue attribution.
  • Payments are matched only within the connecting site. A charge from a site’s Stripe account is attributed to sessions on that same site. Revenue never crosses between sites.
See managing sites for how this fits the wider multi-site model.

How attribution works

When a payment settles, Stripe sends a checkout.session.completed and/or invoice.payment_succeeded webhook to Traceten. Traceten then tries to tie the payment back to the originating session, in priority order:
  1. client_reference_id: if you passed the visitor’s _traceten_vid cookie through as client_reference_id, Traceten matches it to the originating session exactly. This field only exists on checkout.session.completed.
  2. Payment Link redirect: if you set the Payment Link’s confirmation page to your own site with ?session_id={CHECKOUT_SESSION_ID}, the Traceten snippet on that page reports the Checkout Session id and Traceten matches it to the browser that completed the purchase. See below.
  3. Email hash: if the buyer’s session called traceten.identify({ email }), Traceten matches the payment’s customer email (hashed, never stored) to that visitor. This is the only path available to invoice-only and subscription-renewal payments, which never carry a client_reference_id.
  4. No match: the payment is still recorded so revenue totals stay complete, but it shows as unattributed (no AI source).
To get an exact match, give Traceten the visitor id. That’s documented below.
The consumer also accepts metadata.traceten_session_id, checked before client_reference_id if present. It is intentionally not documented as something to send: it pins to whichever session was live when the Checkout Session was created, which skips the visitor-id match’s own resolution, the one that already finds the visitor’s most recent AI-referred session even when a more recent non-AI session exists. For a returning visitor, passing session_id can attribute to the wrong (or no) source in exactly the case this product exists to catch. client_reference_id alone is both sufficient and the better match.
A bare Stripe Payment Link never goes through your server, so there is nowhere to set client_reference_id. Instead, have Stripe hand the Checkout Session id back to your own site after payment, and let the snippet do the rest.
1

Open the Payment Link in Stripe

Stripe dashboard, Product catalogue → Payment Links → your link → Edit.
2

Set the confirmation page to your own site

Under After payment, choose Don’t show confirmation page and pick Redirect to your website (Stripe labels this “Confirmation page”). Enter a page on your site that has the Traceten snippet installed, with the session parameter appended:
{CHECKOUT_SESSION_ID} is Stripe’s own template variable. Stripe replaces it with the real session id when it redirects the buyer. Type it exactly as shown, braces included.
3

Save and buy something through the link

Complete a real purchase. The buyer lands on your thank-you page, the snippet reads session_id out of the URL, and the payment appears against its AI source in Revenue, usually within a minute or two.
Things worth knowing:
  • You change nothing in your code. The only edit is the redirect URL in Stripe.
  • Stripe must be connected to Traceten. Traceten ignores a thank-you-page report for a site whose Stripe account is not connected, and does not hold it until you connect. A report can also be discarded for up to a minute after you connect.
  • The order of events does not matter. Stripe’s webhook usually reaches Traceten before the buyer’s browser finishes redirecting. When that happens the payment is recorded first and re-attributed the moment the browser reports in, on the original payment’s own date.
  • The thank-you page must load within 30 minutes of payment. Traceten pairs a report with a payment only when the two are no more than 30 minutes apart. Stripe’s redirect happens straight after payment, so this only matters for a thank-you page opened again much later.
  • The thank-you page needs the snippet. If the redirect points somewhere the snippet is not installed, nothing is reported and the payment stays unattributed.
  • Payment Links with “Create invoices” turned on work too. Stripe then sends both the checkout and an invoice for one payment. Traceten records the revenue once, against whichever event reaches it first, and matches the thank-you page report to that record either way. If both events are processed at the same moment, the report can miss the pairing and the payment keeps whatever match it already had.
  • One-time payments are covered in both directions; subscriptions only when the browser gets there first. A subscription’s revenue is recorded against the invoice rather than the checkout, and the invoice carries no Checkout Session id, so there is nothing to match a late redirect against. For subscription Payment Links, pass client_reference_id from your own checkout flow where you can.
This path matches a browser, not a person. A buyer who completes checkout on their phone and opens the confirmation link on a laptop is not matched, and neither is a confirmation URL forwarded to somebody else. client_reference_id, where you can use it, is exact.

Guest checkout: pass the visitor ID via client_reference_id

Stripe Checkout, Payment Links, and Pricing Tables capture the buyer’s email for the first time inside Stripe’s hosted page. By then the visitor session has already ended, so there is no email on your domain to match against. The fix is to pass the visitor’s pseudonymous Traceten identifier through to Stripe as client_reference_id when you create the Checkout Session. Stripe surfaces the value on the checkout.session.completed event, and Traceten matches it back to the originating AI session exactly, with no email required.
In the dashboard, open Settings → Integrations, pick Stripe and click Set up with AI. It copies a prompt for your coding assistant that wires the visitor ID into your checkout. The same card shows this site’s Visitor cookie name.
The robust way to get the value is window.traceten.getVisitorId(), called client-side and forwarded to whatever creates the Checkout Session:
If you’d rather not change your client-side request shape, you can read the cookie directly server-side instead:
Do not hardcode this cookie name. It is per-site and only correct on your configured domain (not on localhost). Find the real one under Sites → Settings → Cookies → Visitor cookie name.
Either path is the most reliable attribution method for guest checkouts. Traceten prefers it over email matching whenever it is present: the visitor ID is an exact identifier Traceten generated, while email matching is fuzzy (typos, capitalisation, multiple emails per visitor). No personal data leaves your server on this path: client_reference_id carries only the pseudonymous identifier, which is not PII.
client_reference_id must be 200 characters or fewer (a Stripe limit). The _traceten_vid cookie is a UUID and is well within this limit. If you already use client_reference_id for your own order number, attribution falls back to the email-matching path automatically.
client_reference_id matching covers both one-time Checkout (Stripe Checkout, Payment Links, and Pricing Tables in mode: 'payment', the guest-checkout case above) and subscription Checkout (mode: 'subscription'). For subscriptions, Traceten records the reference when the Checkout completes and applies it to the subscription’s first invoice, so the first charge is attributed exactly once. Set client_reference_id the same way for both. If a subscription has no reference (for example, created directly via the Stripe API or dashboard rather than Checkout), Traceten falls back to email-based matching automatically. Attribution of recurring renewal charges is tracked separately.

Invoice and subscription payments: call identify()

invoice.payment_succeeded events (subscription renewals and manually-issued invoices) never carry a client_reference_id (it is a Checkout Session field). For these payments the only way Traceten can match the revenue to a session is the email hash: call traceten.identify({ email }) during the visitor’s session, using the same email the customer pays with.
Traceten hashes the email before it ever touches storage. The raw email is never logged or stored. When the renewal invoice later settles, Traceten hashes the payment’s customer email the same way and matches the two. Two things are worth being precise about, because “hashed” can imply more than it should. The address does reach Traceten. traceten.identify() sends the email to our edge over HTTPS; the hash is computed there, on our servers, not in the browser. We clear the plaintext from memory immediately and never write it to storage or to a log. But it is transmitted, and a privacy notice that implies otherwise would be wrong. There are two digests, and only one is keyed. Every payment is matched on SHA-256 of the email plus a per-site salt. Alongside it we compute an HMAC-SHA-256 under a per-site key, which is what identify() produces and what is tried first. A salt and a key are different constructions; only the second is keyed. The key is generated for every site when the site is created, so this does not depend on you having called identify() before.
Without an identify() call, a renewal invoice has no session to match and is recorded as unattributed. The subscription’s first charge is still attributed exactly when the original Checkout passed client_reference_id; it is the recurring renewals that need identify() to stay attributed.

Testing your setup

For the fastest reliable test path, use a Stripe Payment Link. The Traceten snippet auto-appends the visitor’s _traceten_vid value to Payment Link URLs on your site, so client_reference_id is populated with no server code. Complete a purchase through a Payment Link and the resulting revenue row should attribute to the originating AI session. The purchase arrives on the webhook endpoint Traceten created during connect, so it should show up within a minute or so of completing checkout. If you create Checkout Sessions server-side instead, you must pass the cookie yourself. See the guest-checkout section above. A server-created session that does not set client_reference_id (and whose visitor never called identify()) will record as unattributed. You can confirm coverage on the dashboard’s Attribution coverage card, which shows what share of revenue Traceten tied back to a session and how much is still unattributed.

Backfilling historical orders

A webhook endpoint only delivers events created after it exists, so anything that happened before you connected has to be imported. After connecting Stripe, Traceten automatically backfills the last 90 days of:
  • completed Checkout Sessions: one-time purchases, Payment Links and subscription checkouts. These carry client_reference_id, so a historical order can be attributed to the exact AI session that produced it.
  • paid invoices: subscription charges and manually issued invoices, attributed by email hash.
Orders older than 90 days are not backfilled. Backfill status is shown on Settings → Integrations for the site. It typically completes within a few hours of connecting.

Rotating the key

To replace the key, create a new restricted key in Stripe with the same permissions, then pick Stripe on Settings → Integrations for that site, click Replace key, and paste it in. Traceten creates the replacement endpoint first, stores it, and only then deletes the one the old key created, so nothing stops delivering in between and stale endpoints do not accumulate on your Stripe account. For a moment both endpoints exist and a payment may be delivered twice; Traceten ignores the duplicate. Your imported history and your backfill are kept; you do not need to disconnect first. Delete the old key in Stripe once the new one shows as connected.

Using a processor we do not integrate with

Stripe, Shopify, Lemon Squeezy, Polar and Paddle have built-in integrations. Dodo has a documented path with no server of yours required. For anything else, including Podia, Kajabi and your own checkout, send payments to the Payment API instead. Do not do both for the same processor. If you connect Stripe here and post Stripe payments to the Payment API, every payment is recorded twice and your revenue is overstated. The two paths deduplicate on different keys and cannot see each other. See avoiding duplicate payments.

Disconnecting Stripe

To remove the Stripe integration:
  1. Open Settings → Integrations in the dashboard and choose the site.
  2. Pick Stripe, then click Disconnect and confirm.
  3. Delete the restricted key in your Stripe dashboard.
Disconnecting deletes the webhook endpoint Traceten created on your Stripe account, then removes the stored key. If you already deleted or revoked the key in Stripe, the disconnect still completes and the stored key is still removed. Disconnecting stops future charge attribution. Historical attributed charges remain in your Traceten data.