03 · Revenue
Connect Lemon Squeezy and link its orders to visits
Tally reads your store's orders and subscription invoices, and adds its own webhook.
01Connect Lemon Squeezy
Create an API key in Lemon Squeezy
Lemon Squeezy's API keys can't be limited, so make one just for Tally: Tally only reads your store's orders and adds its own webhook, and you can delete the key at any time. In Lemon Squeezy, open Settings, API and create a key. This is all Tally does with it:
Permission Access Why Orders Read Every sale: its amount, tax and refunds, and the customer's email (hashed at once, never stored). Subscription invoices Read Renewals and plan changes. Webhooks Write Tally adds its own webhook to the store, so payments arrive within seconds. A key made in test mode sees only test orders: use one to try it first.
Paste the API key
On your site's Integrations page (or the Payments step when you add a site), open Lemon Squeezy and fill in:
- API key: Tally stores it encrypted and never shows it again.
- Store (optional): Only if your key sees more than one store: its address, name or id.
Then press Connect Lemon Squeezy.
Tally checks it and imports the past year
It checks the key against your store, adds its own webhook so new orders arrive as they happen, and imports the past 365 days. Lemon Squeezy sends a checkout's custom data only in webhooks, so imported orders are linked by the customer's email; orders from now on carry the visitor id.
02Pass the visitor's id as custom data
On pages with buy links, a few lines add the _tally_vid cookie's value as checkout[custom][tally_visitor_id] to every Lemon Squeezy checkout link. A checkout your server creates takes it in checkout_data.custom.
// On pages with Lemon Squeezy buy links: add the visitor id as custom data.
const visitorId = (document.cookie.match(/(?:^|; )_tally_vid=([\w-]+)/) || [])[1];
for (const link of document.querySelectorAll('a[href*=".lemonsqueezy.com/checkout/buy/"]')) {
const url = new URL(link.href);
if (visitorId && !url.searchParams.has('checkout[custom][tally_visitor_id]')) {
url.searchParams.set('checkout[custom][tally_visitor_id]', visitorId);
link.href = url.href;
}
}
// Express with cookie-parser, where you create a Lemon Squeezy checkout.
app.post('/checkout', async (req, res) => {
const visitorId = req.cookies['_tally_vid'];
const response = await fetch('https://api.lemonsqueezy.com/v1/checkouts', {
method: 'POST',
headers: {
Accept: 'application/vnd.api+json',
'Content-Type': 'application/vnd.api+json',
Authorization: 'Bearer ' + process.env.LEMONSQUEEZY_API_KEY,
},
body: JSON.stringify({
data: {
type: 'checkouts',
attributes: { checkout_data: { custom: visitorId ? { 'tally_visitor_id': visitorId } : {} } },
relationships: {
store: { data: { type: 'stores', id: '1' } },
variant: { data: { type: 'variants', id: '1' } },
},
},
}),
});
const { data } = await response.json();
res.redirect(303, data.attributes.url);
});
04Check it worked
05What isn't read
Something missing, or not what you see? Write to support@tallybyoutpost.com and say which page.