GET /api/config/{siteId} returns the settings the browser snippet needs. It is public: site IDs are identifiers, not secrets, and the response never contains tenant, billing, user or visitor data. Schema: /schemas/site-config.v1.json.
Enabled response
{
"schemaVersion": 1,
"siteId": "tl_k3j9x2m8n4p7q1r5s6t0",
"version": 3,
"enabled": true,
"attributionLifetimeDays": 90,
"consentMode": "explicit",
"consentPreset": "custom",
"lastTouch": false,
"fieldMappings": [
{ "field": "utm_source", "selector": "input[name=\"tl_utm_source\"]" },
{ "field": "channel", "selector": "input[name=\"tl_channel\"]" }
],
"suppressedQueryParams": ["email", "token", "password"],
"collect": { "sessions": true, "leads": true }
}
Disabled response
{ "schemaVersion": 1, "siteId": "tl_k3j9x2m8n4p7q1r5s6t0", "version": 3, "enabled": false, "reason": "site_paused" }
reason is one of site_paused, subscription_inactive, domain_not_allowed, not_found (HTTP 404). The snippet must stay inert whenever enabled is false or the request fails.
Semantics
- Domain check: the host from the
Originheader (fallbackReferer) must match a registered domain (include_subdomainshonoured). Requests without either header are served. This is abuse reduction, not authentication. - Subscription: served when the team has an active/grace-period subscription or a generic trial.
TRACELEAD_REQUIRE_SUBSCRIPTION=falsedisables the check for local development. - Caching:
Cache-Control: public, max-age=300, s-maxage=300(configurable viaTRACELEAD_CONFIG_MAX_AGE),ETag(304 onIf-None-Match),Vary: Origin. A paused or cancelled site is therefore served for at most 5 minutes after the change. Not-found responses cache for 60 s. - CORS:
Access-Control-Allow-Origin: *so the snippet can always read the response, including the disabled reason. - Rate limit: 120 requests/minute per IP (
TRACELEAD_CONFIG_RATE_LIMIT), HTTP 429 beyond that. - Collection flags:
collect.sessions/collect.leadstell the snippet which consent-gated events it may send toPOST /api/collect(seedocs/collect-api.md, ADR-021). Absent flags mean "send nothing". - Consent preset:
consentPresetnames the consent manager whose marketing category the snippet follows (ADR-032):custom(the page callstracelead('consent', …)itself),cookiebot,onetrust,cookieyes,complianz,iubenda,usercentricsorconsent-mode. Absent or unknown values meancustom. See Consent integration. - Last touch:
lastTouch(boolean) tells the snippet to keep a second, most-recent-non-direct record undertl_lt_<siteId>and to fill thelast_*fields (ADR-026). Absent means off. - Field contract:
fieldvalues are theApp\Enums\AttributionFieldcases (the first-touch fields and theirlast_*counterparts); the default selector isinput[name="tl_<field>"]. The snippet fills only existing elements matching a selector (ADR-004).