Theme

Start for free
DocsPolar

03 · Revenue

Connect Polar and link its orders to visits

Tally reads Polar's orders, refunds and lost disputes with a token that has only the scopes it needs.

01Connect Polar

  1. Create an access token in Polar

    Tally only reads your orders; it never needs to move money. In Polar, open Settings, Access Tokens, create an organization access token, and give it only these scopes:

    PermissionAccessWhy
    Organizationsorganizations:readThe organization's name, so you can tell which one is connected.
    Ordersorders:readEvery payment: its amount, tax, metadata and the customer's email (hashed at once, never stored).
    Refundsrefunds:readRefunds.
    Disputesdisputes:readChargebacks: disputes you lost.
    Webhookswebhooks:writeTally adds its own webhook endpoint, so payments arrive within seconds.

    A sandbox token works too: Tally tries Polar's sandbox when production doesn't know the token.

  2. Paste the access token

    On your site's Integrations page (or the Payments step when you add a site), open Polar and fill in:

    • Access token: It starts with polar_oat_. Tally stores it encrypted and never shows it again.

    Then press Connect Polar.

  3. Tally checks it and imports the past year

    It names any scope the token is missing, adds its own webhook endpoint so new orders arrive as they happen, and imports the past 365 days, refunds and lost disputes included.

02Pass the visitor's id in metadata

Where your server creates the checkout, read the _tally_vid cookie and pass it as tally_visitor_id in metadata: Polar copies it to the order and the subscription, so renewals follow. On a checkout link, add the visitor's id as reference_id, which Polar keeps in the checkout's metadata.

Show the code for

// Express with cookie-parser, where you create the Polar checkout.
app.post('/checkout', async (req, res) => {
  const visitorId = req.cookies['_tally_vid'];
  const response = await fetch('https://api.polar.sh/v1/checkouts/', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + process.env.POLAR_ACCESS_TOKEN,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      products: ['your-product-id'],
      metadata: visitorId ? { 'tally_visitor_id': visitorId } : {},
      success_url: 'https://example.com/thanks?checkout_id={CHECKOUT_ID}',
    }),
  });
  const checkout = await response.json();
  res.redirect(303, checkout.url);
});

// On pages with Polar checkout links: add the visitor id as reference_id. Polar's embed
// marks its links with data-polar-checkout; use your own selector for plain links.
const visitorId = (document.cookie.match(/(?:^|; )_tally_vid=([\w-]+)/) || [])[1];
for (const link of document.querySelectorAll('a[data-polar-checkout]')) {
  const url = new URL(link.href);
  if (visitorId && !url.searchParams.has('reference_id')) {
    url.searchParams.set('reference_id', visitorId);
    link.href = url.href;
  }
}

03On a cookieless site

With no cookie there's no id to pass, so identify the buyer with the email they pay with: goals and identify.

04Check it worked

05What isn't read

Something missing, or not what you see? Write to support@tallybyoutpost.com and say which page.