SDK reference
One script, five calls. No cookies, no identifiers, and a failed ad never blocks your page.
Load
Put a container where the ad goes and load the script once. The container id is adjoin- plus the placement id.
<!-- one script per page; data-placement renders that slot automatically -->
<div id="adjoin-plc_ab12" data-format="card"></div>
<script async src="https://cdn.adjoin.dev/v1.js" data-placement="plc_ab12"></script>- data-placement
- Renders this placement as soon as the page is ready.
- data-format
- On the container: banner, card, inline_native, sidebar or newsletter. Default card.
- data-tenant
- Partner slug, when you came through a partner (see Partner integration).
- data-analytics="off"
- Turns off the free pageview analytics.
Sizes: banner 728×90 · card 320×250 · inline_native 600×120 · sidebar 300×250 · newsletter 600×200
adjoin.render(options)
Renders a placement into its container. The slot height is reserved first, so there is no layout shift, and the ad lives in a Shadow DOM so your CSS and ours never meet. If nothing fits, the slot collapses to zero height.
adjoin.render({
placement: 'plc_cd34', // required — the id from your dashboard
format: 'sidebar', // banner | card | inline_native | sidebar | newsletter
onEmpty: () => showMyOwnPromo() // optional — called when the slot collapses
});adjoin.trigger(moment, options?)
Marks a moment in your app — signup_complete, empty_state, quota_hit and so on. It is recorded even without a placement, so your funnel still counts if you turn ads off.
// record a moment; with a placement it also renders an ad there
adjoin.trigger('signup_complete', { placement: 'plc_ab12' });adjoin.pageview(path?)
Free analytics. Only the first two path segments are sent, and ids in them are replaced with :id.
// sent automatically on load; call it yourself on client-side route changes
adjoin.pageview('/pricing');
<!-- or turn analytics off -->
<script async src="https://cdn.adjoin.dev/v1.js" data-analytics="off"></script>adjoin.segment(label, on?)
Audience labels. Tag the current session with a word that describes it — plan, role, stage. Advertisers can target or exclude a label, so labelled inventory earns more. The label lives in this tab only (sessionStorage) and is sent only with this site's ad requests and pageviews; it never follows a visitor to another site. Only per-label pageview totals are stored. Format: lowercase letters, digits, _ : -, up to 32 characters, no runs of 6+ digits. Up to 5 per session. A label becomes targetable once it reaches 100 pageviews across the network in 7 days.
// label this visitor's session — plain words, never ids or emails
adjoin.segment('plan:free');
adjoin.segment('role:developer');
adjoin.segment('plan:free', false); // remove one
adjoin.segment(null); // remove all
// suggested labels: plan:free · plan:paid · plan:trial · role:developer · role:designer
// role:founder · stage:onboarding · stage:active · intent:upgradeadjoin.convert(valueUsd?, orderId?)
For advertisers on CPA or affiliate. Call it on your own site after a purchase. The click token from the ad is kept in this tab only (sessionStorage), so it links conversions within the same visit. Pass an order id: the same order is never charged twice, and you can refund it by that id. For conversions days later, use server-to-server (REST API).
// on your own site, after checkout
adjoin.convert(29.00, 'order_1234'); // value in USD, your order idPrivacy
No cookies, no localStorage, no fingerprinting. Visitors are counted with a hash that changes every day, so the same person cannot be linked from one day to the next.
Questions: [email protected]