Adducta Your ad team, without the team Back to the site

Developers

Integrating with Adducta

Everything a site or a platform needs to connect to Adducta: one script on the page, an attribute on the forms that matter, and one request from your server when something worth paying for happens. It is the same for every business we advertise and every platform we work with. Nothing here was built for one of them.

API at api.adducta.com Reference at api.adducta.com/docs Updated 24 September 2026

The short version

The sections below are the detail.

01Clients and partners

There are two ways to work with Adducta, and they are different things.

One company can be both: a platform we advertise, whose users also advertise through us. It then holds one key for each role, and neither stands in for the other. The partner key cannot report for the company's own site, and the client key cannot act for its users. The two look alike, so ask the API which one you are holding:

GET https://api.adducta.com/v1/me
Authorization: Bearer adu_live_...

{ "kind": "integration", "key": "client", "clients": [ { "name": "..." } ] }

02The tag

On every page an ad can land on, in the head or just before the end of the body:

<script src="https://adducta.com/attribution.js"
        data-site-key="adu_site_..."></script>

It gives each visitor a random id, kept in the browser's local storage under adu_visitor, and keeps the first ad click that brought them (gclid, gbraid, wbraid, fbclid, msclkid and the utm_ tags) for 90 days, which is as long as the ad platforms take a conversion afterwards. It sets no cookies, loads nothing else, and never throws: a failure in it leaves your page exactly as it was.

The site key is public on purpose. It can record an arrival and the events your catalogue allows from a page, and it can read nothing.

Your sites and their keys, each with the exact line to paste, are at GET /v1/sites. To give a new site its key:

POST https://api.adducta.com/v1/clients/{client_id}/sites
Authorization: Bearer adu_live_...

{ "label": "plumber.example" }

201 { "key": "adu_site_...", "tag": "<script src=...></script>", ... }

A client holds at most 20 live sites. POST /v1/sites/{id}/revoke switches one off; what it recorded stays.

03Forms

The visitor id is what joins an account to the ad click that brought it. Keep it with the account, and everything in section 06 becomes possible. The simplest way is an attribute on the form that creates the account or the enquiry:

<form action="/signup" method="post" data-adu-form>
  ...
</form>

As the form submits, the tag adds hidden fields: visitor_id, and the click identifiers and utm_ tags if an ad brought the visitor. It works whether the browser posts the form or your own script reads it into FormData, because the tag runs before your handlers do. A field you already have with the same name is left alone.

From a script instead, call window.adducta.decorate(form) before submitting, or read the values directly with window.adducta.attribution(), which returns { visitor_id, gclid, ... }.

04What the page can see

Some things worth paying for happen on the page: somebody used your calculator, or went to sign up. Name them in your catalogue (see section 07), then mark them in the page:

<a href="/signup" data-adu-event="trial_start">Try it free</a>

<form data-adu-event="quote_requested"> ... </form>   <!-- on submit -->

window.adducta.event("calculator_used");

On a link or a button the event is the click, on a form it is the submission, and elements added to the page later work the same way. Each name is sent once per page view, and recorded once per visitor for ever: a button pressed five times is one person deciding once.

The endpoint behind this answers the same to everything, including a name that is not in your catalogue, because it is public and a difference would let anybody map your funnel. To check what arrived, see section 09.

05Enquiries

An enquiry is somebody handing over their details. Send it to us as well as to yourself, and we grade it before we tell the ad platform what it was worth:

POST https://api.adducta.com/v1/intake/enquiry
Content-Type: application/json

{
  "site_key": "adu_site_...",
  "visitor_id": "<window.adducta.attribution().visitor_id>",
  "name": "...", "email": "...", "phone": "...",
  "message": "What they asked for, in their words",
  "gclid": "...", "utm_source": "...", "utm_term": "..."
}

A message matters more than it looks. The grade is read from what the person said and did, and an empty message reads as somebody who said nothing. If your form asks nothing, say what they did: "Created an account and started the free trial."

This endpoint is public like the tag, and it allows 60 requests an hour from one address. That suits a page. A server forwarding many enquiries from one address should tell us, and we will give it a route of its own under its key.

06What happens after the page

The things you actually want more of happen later, on your own server: the account that gets used, the plan that gets paid for, the job that gets booked, the quote that gets accepted. Report them, and the ad platform learns to find more people like the ones who did them, instead of more people who press buttons.

POST https://api.adducta.com/v1/outcomes
Authorization: Bearer adu_live_...
Content-Type: application/json

{
  "visitor_id": "3f2b8c1e-9a4d-4e2b-8f1a-0c5d7e9b1a2c",
  "name": "subscription_paid",
  "site_key": "adu_site_...",
  "occurred_at": "2026-09-24T14:03:11Z"
}

Nothing else is accepted, and a field this contract does not have is refused rather than ignored. There is no value field on purpose: what each outcome is worth is in your catalogue.

Unlike the page endpoints, this one tells you what happened, because the caller holds your key:

AnswerMeaning
201, recorded: trueCounted. If ad_click_at is set, an ad click brought this visitor and it goes to the ad platform within the hour.
201, ad_click_at: nullCounted in Adducta's own numbers, but no ad click is known for this visitor on this site, so no ad platform hears of it. If you expected one, the visitor id kept with the account is not the one the tag gave them.
200, recorded: falseAlready recorded for this visitor. Each outcome counts once per visitor, for ever, so reporting it again is safe, and is a fine way to retry.
404None of the sites your key reaches has that site key.
422The name is not in the catalogue, the time is out of range, the key reaches several sites and you did not say which, or the body has a field it should not.
403Sent with a person's login rather than a key. Outcomes come from servers.

Outcomes worth money are marked server only in the catalogue. The page cannot report them, because the site key is in the page for anyone to read, and a paid plan must not be something anybody can claim.

07The catalogue

Every site has a catalogue of the things it may report. Each entry carries:

The values are the safety of counting several things at once. A calculator used fifty times a day at 5 cannot outvote one paid plan at 1,000, so the ad platform chases the right people. That is why they are agreed with us rather than sent in a request, and why a new entry is added before the first report of it rather than because of one. Tell us what your business counts as progress and we will propose the catalogue, with the reasons, for you to agree.

08For partner platforms

A partner's users advertise through Adducta from inside the partner's own product and never see ours. Everything above works the same for them, with the partner key in place of a client key and the site_key of the user's site on every server call. What the partner key can do today:

StepRoute
See your own position: clients, limits, balanceGET /v1/partner/me, GET /v1/partner/ledger
Bring a user as an advertiserPOST /v1/partner/clients with name and an optional daily_cap_cents
Change what they may spend a dayPATCH /v1/partner/clients/{id}
Give a user's site its keyPOST /v1/clients/{id}/sites with a label; the answer carries the tag to put on it. GET /v1/sites?client_id=... lists them
Ask for a campaignPOST /v1/campaigns/plan?client_id=... with the brief: business, offer, geography, landing page, daily budget, and what makes a lead worth calling
Read the plan, and approve itGET /v1/campaigns/{id}/plan, then POST /v1/campaigns/{id}/submit, which is the user's approval
Build it at the ad platform, pausedPOST /v1/campaigns/{id}/launch. Takes up to a minute; a second call while the first is building answers 409 instead of building twice. The answer lists anything still in the way of starting
Start and stopPOST /v1/campaigns/{id}/start, /pause; a whole advertiser with POST /v1/clients/{id}/pause and /resume
Read resultsGET /v1/campaigns, /v1/leads, /v1/spend, /v1/decisions
Answer the agentPOST /v1/campaigns/{id}/offers/{decision_id}, POST /v1/campaigns/{id}/tell
Report what happened after the pagePOST /v1/outcomes, with the user's site_key

Graded leads arrive at your endpoint as they happen, as a signed lead.graded webhook: X-Adducta-Event, X-Adducta-Delivery, X-Adducta-Timestamp and X-Adducta-Signature, which is sha256= and an HMAC of the timestamp, a dot and the raw body, with the secret you were given once.

A partner is also the best source of outcomes there is. A CRM knows which of its users' leads became deals, a booking system knows which appointments were kept, and reporting those against the visitor who arrived from an ad is worth more to the auction than anything a page can say.

Still done by us, for now

These steps are real and quick, and today a person at Adducta does them when you ask. Each will become a route; this page will say so the day it does.

  1. Opening the ad account a user's campaigns run in.
  2. Agreeing a site's catalogue.
  3. Registering your webhook endpoint, and events beyond lead.graded.
  4. Topping up a partner balance, which follows a bank transfer.

09Checking that it works

Anything unclear, or a route you need that is not here: hello@adducta.com.