All endpoints

DocsAPISites

GET/sites/{siteId}/install

Read 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 pathrequired

    The 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

  • siteIdstringrequired
  • siteKeystringrequired

    The 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.

  • regionstringrequired

    One of EU, US

  • snippetstringrequired

    The 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 | nulloptional

    The 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.

  • deliveryOriginstringrequired

    Where the client artifacts are served from, for a Content-Security-Policy entry.

  • rulesetUrlstringrequired

    The site's compiled ruleset, which the payload fetches.

  • consentIngestUrlstringrequired
  • monitoringIngestUrlstringrequired
  • partsobjectrequired

    THE 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.configJsstringrequired

    Empty since 2026-09-29: the site's config rides in the bootstrap's call. Emit it first if non-empty.

  • parts.bootstrapJsstringrequired

    The inline bootstrap, its call carrying the site's config. Inline, first.

  • parts.tcfStubJsstring | nullrequired
  • parts.consentModeJsstringoptional

    Present only when the site opts into Consent Mode extras. Inline, after the bootstrap.

  • parts.blockerobjectoptional

    Present 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.urlstringrequired
  • parts.blocker.integritystringrequired
  • parts.payloadobjectrequired
  • parts.payload.kindstringrequired

    Always tag

  • parts.payload.urlstringrequired
  • parts.payload.integritystringrequired
  • parts.payload.jsstringrequired
  • readybooleanrequired

    False when something upstream of the paste is missing -- most often that no ruleset has been published yet. problems says which, in words meant for a developer.

  • problemsarray of stringrequired
  • monitoringReadybooleanoptional

    True when siteTag runs EVENT MONITORING as soon as it is on the page, whatever ready says 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).

  • cspobjectoptional

    What a strict Content-Security-Policy must allow for this install. Absent when ready is false. Merge each list into your own directive; nothing here replaces your policy.

  • csp.originsarray of stringrequired

    Every origin our client loads from or sends to.

  • csp.siteTagScriptSrcarray of string | nullrequired

    script-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 stringrequired

    script-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 stringrequired

    The ruleset, consent decisions and tag events.

  • csp.imgSrcarray of stringrequired

    The owner's logo on a paid Consent banner.

  • csp.styleSrcarray of stringrequired

    The 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