/sites/{siteId}/installRead the install snippet
Everything needed to install this site: the exact snippet bytes, the public site key, the delivery origin for a CSP entry, and the URLs the client will talk to.
Auth header
Authorization: Bearer tsk_live_…The key needsinstall:read
Parameters
siteIdstringin pathrequiredThe site id, from GET /v1/sites.
Example request
curl "https://app.tagsentry.ai/api/v1/sites/3f6c1b8e-2d4a-4c7e-9a51-0b8f2e6d7c10/install" \ -H "Authorization: Bearer tsk_live_…"Response 200
siteIdstringrequiredsiteKeystringrequiredThe site's public key. It appears in every visitor's HTML, so it is public by construction -- put it in a client-side environment variable, not a secret store.
regionstringrequiredOne of
EU,USsnippetstringrequiredThe exact bytes to place in <head>. ORDER IS THE WHOLE CONTRACT: this goes ABOVE the tag manager snippet and below nothing except whatever sets the visitor's jurisdiction. It never loads, defers or gates gtm.js.
siteTagstring | nulloptionalThe one-line install (the default since 2026-09-29): one synchronous script tag naming this site's script, with a fail-closed onerror. Same place as
snippet, never async or defer.deliveryOriginstringrequiredWhere the client artifacts are served from, for a Content-Security-Policy entry.
rulesetUrlstringrequiredThe site's compiled ruleset, which the payload fetches.
consentIngestUrlstringrequiredmonitoringIngestUrlstringrequiredpartsobjectrequiredTHE SAME SNIPPET, IN PIECES, for a caller with no HTML file to paste into -- a React root layout, a template helper, a framework plugin. Emit them in the order the order the snippet uses: config, bootstrap, blocker and Consent Mode line if present, TCF stub if present, payload last. The two inline pieces must run before anything else on the page and make no network request; the payload is async and must never block. Getting that wrong does not break the page -- it breaks the Consent Mode race the bootstrap exists to win, silently, on somebody else's visitors.
parts.configJsstringrequiredEmpty since 2026-09-29: the site's config rides in the bootstrap's call. Emit it first if non-empty.
parts.bootstrapJsstringrequiredThe inline bootstrap, its call carrying the site's config. Inline, first.
parts.tcfStubJsstring | nullrequiredparts.consentModeJsstringoptionalPresent only when the site opts into Consent Mode extras. Inline, after the bootstrap.
parts.blockerobjectoptionalPresent only when the site blocks trackers outside Tag Manager. A SYNCHRONOUS script element with this src, integrity and crossorigin="anonymous", directly after the bootstrap -- never async, deferred or injected -- or hardcoded trackers run before consent. (Until 2026-09-29 this was
blockerJs, an inline script.)parts.blocker.urlstringrequiredparts.blocker.integritystringrequiredparts.payloadobjectrequiredparts.payload.kindstringrequiredAlways
tagparts.payload.urlstringrequiredparts.payload.integritystringrequiredparts.payload.jsstringrequiredreadybooleanrequiredFalse when something upstream of the paste is missing -- most often that no ruleset has been published yet.
problemssays which, in words meant for a developer.problemsarray of stringrequiredmonitoringReadybooleanoptionalTrue when
siteTagruns EVENT MONITORING as soon as it is on the page, whateverreadysays about the banner: the same one line serves both products, so a monitoring-only site installs exactly this line too (it shows no banner while the banner is offline or another consent tool runs the page, and monitors only where the visitor's consent allows).cspobjectoptionalWhat a strict Content-Security-Policy must allow for this install. Absent when
readyis false. Merge each list into your own directive; nothing here replaces your policy.csp.originsarray of stringrequiredEvery origin our client loads from or sends to.
csp.siteTagScriptSrcarray of string | nullrequiredscript-src for the one-line tag: the origins,
'unsafe-hashes'and the hash of its onerror handler. The hash is the same on every site. A nonce cannot cover the onerror attribute.csp.snippetScriptSrcarray of stringrequiredscript-src for the inline snippet: the origins and one
'sha256-…'per inline script block. These hashes are this site's (its key is in the bootstrap's call) and change when the snippet does: re-read them after a banner or blocking change.csp.connectSrcarray of stringrequiredThe ruleset, consent decisions and tag events.
csp.imgSrcarray of stringrequiredThe owner's logo on a paid Consent banner.
csp.styleSrcarray of stringrequiredThe banner puts a <style> element in its shadow root, so style-src needs 'unsafe-inline'.
Example response
{ "siteId": "3f6c1b8e-2d4a-4c7e-9a51-0b8f2e6d7c10", "siteKey": "…", "region": "EU", "snippet": "…", "siteTag": "…", "deliveryOrigin": "…", "rulesetUrl": "https://silverpine.example/", "consentIngestUrl": "https://silverpine.example/", "monitoringIngestUrl": "https://silverpine.example/", "parts": { "configJs": "…", "bootstrapJs": "…", "tcfStubJs": "…", "consentModeJs": "…", "blocker": { "url": "https://silverpine.example/", "integrity": "…" }, "payload": { "kind": "tag", "url": "https://silverpine.example/", "integrity": "…" } }, "ready": false, "problems": [ "…" ], "monitoringReady": false, "csp": { "origins": [ "…" ], "siteTagScriptSrc": [ "…" ], "snippetScriptSrc": [ "…" ], "connectSrc": [ "…" ], "imgSrc": [ "…" ], "styleSrc": [ "…" ] }}More about this endpoint
ORDER IS THE CONTRACT. The snippet goes in <head>, ABOVE the tag manager snippet, and below nothing except whatever sets the visitor's jurisdiction. It never loads, defers or gates gtm.js -- a total delivery failure of ours leaves the customer with a loaded container, denied Consent Mode defaults and no banner, which is fail-closed and visible.
This call MINTS what it has to: a site that has never had a public key gets one here, and a site with no ruleset configuration gets the default one published. That is a write on a GET, deliberately -- the customer's whole obligation is to paste one snippet, and an endpoint that asked them to press Generate first would have added an obligation to get the thing that was supposed to be their only one.
STRICT CONTENT-SECURITY-POLICY. csp lists what to allow, read off these exact bytes. The one-line siteTag has an inline onerror that sets denied Consent Mode defaults if our script cannot load; a policy admits it only with 'unsafe-hashes' and its hash (csp.siteTagScriptSrc, the same on every site). Or use the inline snippet, which makes no request before your tags and needs one hash per inline block (csp.snippetScriptSrc, this site's own, changed by any banner or blocking change). The tag also works with its onerror removed, and then it FAILS OPEN: if our script cannot load (an outage, a blocked host), nothing sets the denied Consent Mode defaults, Google tags read the unset signals as granted, and they fire with full storage on every page view until the script loads again. Only the Tag Manager consent template, which sets its own denied defaults inside the container, still holds the line. Prefer the hash, or the inline snippet. Either way the banner needs style-src 'unsafe-inline' and the origins in script-src, connect-src and img-src.
Errors401 · 403 · 404 · 500
401Missing, malformed, unknown, revoked or expired API key. These are deliberately indistinguishable in the response -- distinguishing them would confirm to a caller that a token was once real.403The key authenticated but does not carry the scope(s) this operation requires, or (`domain_not_verified`) the site's domain is not verified, so its consent records are not released.404No such resource on this account. A site id belonging to a DIFFERENT account answers 404, never 403 -- a 403 would confirm the id exists somewhere.500Something failed on our side. The requestId in the body is what to quote.
Every error has the same body: { error: { code, message, requestId } }.
From the OpenAPI document, version 2026-08-26. Raw OpenAPI