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
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:
Permission Access Why Organizations organizations:read The organization's name, so you can tell which one is connected. Orders orders:read Every payment: its amount, tax, metadata and the customer's email (hashed at once, never stored). Refunds refunds:read Refunds. Disputes disputes:read Chargebacks: disputes you lost. Webhooks webhooks:write Tally 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.
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.
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.
// 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;
}
}
04Check it worked
05What isn't read
Something missing, or not what you see? Write to support@tallybyoutpost.com and say which page.