@adopt-tech/provisioning
v1.1.0
Published
Type-safe JS toolkit for creating and managing Adopt entities, configurations, and subscriptions
Maintainers
Readme
@adopt-tech/provisioning
Type-safe JS/TS toolkit for programmatic provisioning of Adopt CMP entities via the Hasura GraphQL Actions API.
Prerequisites
To use this SDK you need an API account on the Adopt platform. Contact the AdOpt team at [email protected] to request access.
Overview
adopt-provisioning lets you create and configure the full Adopt entity hierarchy in a single idempotent run:
Organization
└── Disclaimer
├── Options (GCM, consent TTL, languages)
├── Style (colors, theme, position)
├── Terms document
├── Privacy document
├── Cookie document (URL, upload, or auto-generated from scan)
├── Optout / DSAR page
├── Vendors
├── TCF
└── Scan
Members (per organization)Key features
- Idempotent runs — re-running with the same state ID resumes from where it left off
- Rollback on error — deletes every entity created in the run on failure
maxConcurrency: 1— controls execution order of sibling provisioners- Fetcher API — read back any provisioned entity for verification
getInstallationSnippet(disclaimerId)— generates the<head>snippet for banner installation
Partner integration
For the complete end-to-end provisioning flow (login, org, member, disclaimer, style, docs, scan, cookie doc auto-generation) see examples/partner-integration.ts.
export [email protected]
export ADOPT_USER_PASSWORD=yourpassword
# Dry-run — no API calls, shows planned tree
npx ts-node examples/partner-integration.ts --dry-run
# Real provisioning
npx ts-node examples/partner-integration.tsEdit ORG_NAME, ORG_PATH, DOMAIN, and ADMIN_EMAIL at the top of the file per tenant. Each run auto-generates a unique state ID — no risk of skipping provisioners from a previous run.
One-call provisioning (createFullDisclaimer)
The fastest way to provision a fully configured disclaimer. A single server-side call creates the org, disclaimer, style, documents, and optionally a DSAR portal — returning the IDs and install snippet immediately.
const client = new AdoptProvisioningClient({
environment: 'staging',
username: process.env.ADOPT_USER_EMAIL!,
password: process.env.ADOPT_USER_PASSWORD!,
})
const [result] = await client.createFullDisclaimer({
urls: ['https://yoursite.com'],
immediateAvailable: true, // banner loads without waiting for a scan
skipBranding: true, // return immediately, trigger scan in background
org: {
orgPathname: 'your-org', // unique slug; auto-derived from hostname if omitted
orgName: 'Your Company',
// orgId: 'existing-uuid', // use an existing org instead of creating one
},
style: {
mainColor: '#00DD80',
textColor: '#000000',
backgroundColor: '#ffffff',
},
documents: {
privacyUrl: 'https://yoursite.com/privacy',
cookieUrl: 'https://yoursite.com/cookies',
termsUrl: 'https://yoursite.com/terms',
// omit any URL to auto-generate the document from a questionnaire
},
dsar: {
createDsar: true,
// dpo: '[email protected]', // defaults to authenticated user when omitted
inviteDpoAsAdmin: true, // invite the DPO to the org as admin if not already a member
},
})
console.log(result.disclaimerId) // UUID
console.log(result.dashboardUrl) // https://app.goadopt.io/org/…/disclaimer/…
console.log(result.installCode) // <meta …/> + <script …/>When to use
createFullDisclaimervs the provisioner tree
- Use
createFullDisclaimerfor simple, one-shot onboarding (e.g. partner integrations that spin up a banner per customer).- Use the provisioner tree (
ResumableProvisioningExecutor) for complex scenarios that need resume, rollback, incremental updates, or fine-grained control over each entity.
Full runnable example: examples/create-full-disclaimer.ts
Installation
npm install @adopt-tech/provisioningAuthentication
Two modes are supported:
// Token mode — provide a pre-existing JWT (no auto-refresh)
const client = new AdoptProvisioningClient({
environment: 'staging', // or 'production'
token: process.env.ADOPT_USER_TOKEN,
})
// Credentials mode — auto-refreshes when JWT expires (recommended for long runs)
const client = new AdoptProvisioningClient({
environment: 'staging',
username: process.env.ADOPT_USER_EMAIL,
password: process.env.ADOPT_USER_PASSWORD,
})Environment variables
| Variable | Required | Description |
|---|---|---|
| ADOPT_USER_EMAIL | credentials mode | User email for auto-login |
| ADOPT_USER_PASSWORD | credentials mode | User password for auto-login |
| ADOPT_USER_TOKEN | token mode | Pre-existing Cognito JWT |
| ADOPT_HASURA_URL | if no environment | Hasura GraphQL endpoint |
| ADOPT_BACKEND_URL | if no environment | Backend URL for credentials login |
| ADOPT_HASURA_ROLE | No | Hasura role (default: api) |
| ADOPT_HUB_URL | No | Hub base URL (auto-set by environment) |
environment: 'staging' or 'production' sets all URLs automatically (hasuraUrl, backendUrl, hubUrl).
Quick start
import {
AdoptProvisioningClient,
ResumableProvisioningExecutor,
OrganizationProvisioner,
DisclaimerProvisioner,
DisclaimerStyleProvisioner,
TermsDocumentProvisioner,
ScanProvisioner,
CookieDocumentProvisioner,
} from '@adopt-tech/provisioning'
const client = new AdoptProvisioningClient({
environment: 'staging',
username: process.env.ADOPT_USER_EMAIL!,
password: process.env.ADOPT_USER_PASSWORD!,
})
const org = new OrganizationProvisioner('my-org', {
name: 'Acme Corp',
pathName: 'acme-corp',
})
const disclaimer = new DisclaimerProvisioner('my-disclaimer', {
name: 'Cookie Banner',
domains: ['https://acme.com'],
})
disclaimer.addChild(
new DisclaimerStyleProvisioner('my-style', {
primaryColorLight: '#00DD80',
positionController: 'left',
disclaimerTheme: 'modern',
}),
)
disclaimer.addChild(
new TermsDocumentProvisioner('my-terms', {
language: 'pt',
organizationId: '', // resolved from parent org at runtime
url: 'https://acme.com/terms.pdf',
}),
)
// Scan runs before cookie doc so autoGenerate can resolve the scan ID
disclaimer.addChild(new ScanProvisioner('my-scan', { mode: 'fire-and-forget' }))
disclaimer.addChild(
new CookieDocumentProvisioner('my-cookie', {
language: 'pt',
organizationId: '',
autoGenerate: true,
documentName: 'Cookie Policy',
}),
)
org.addChild(disclaimer)
const executor = new ResumableProvisioningExecutor({
client,
config: { rollbackOnError: true, maxConcurrency: 1 },
})
const result = await executor.execute({ organizations: [org] })Installation snippet
After provisioning, get the HTML snippet to inject on the tenant's website:
import { getInstallationSnippet } from '@adopt-tech/provisioning'
const { html } = getInstallationSnippet(disclaimerId)
// <meta name="adopt-website-id" content="..." />
// <script src="//tag.goadopt.io/injector.js?website_code=..." class="adopt-injector"></script>Executor shortcuts
Instead of building full provisioner trees, update a single entity on an existing disclaimer or org:
const executor = new ResumableProvisioningExecutor({ client, config: {} })
// Update style on an existing disclaimer
await executor.executeForDisclaimer(disclaimerId, [
new DisclaimerStyleProvisioner('style', { primaryColorLight: '#00DD80' }),
])
// Update options on an existing disclaimer
await executor.executeForDisclaimer(disclaimerId, [
new DisclaimerOptionsProvisioner('opts', { googleConsentMode: true, consentTTLDays: 30 }),
])
// Provision a new member on an existing org
await executor.executeForOrganization(organizationId, [
new MemberProvisioner('admin', { email: '[email protected]', role: MemberRole.Admin }),
])Fetcher API
Read back any entity after provisioning:
const fetcher = createFetcher(client)
const discFetcher = fetcher.disclaimers({ id: disclaimerId })
// Style
const style = await discFetcher.getStyle()
// style.primary_color_light, style.disclaimer_css.styles.web.card (fonts)
// Options
const opts = await discFetcher.getOptions()
// opts.consent_ttl_in_days, opts.hide_disclaimer, opts.google_consent_mode
// Linked documents (privacy, cookies, terms master IDs)
const docs = await discFetcher.getDocuments()
// Document content URL (the URL the user configured)
const url = await fetcher.documentContentUrl(docs.document_master_privacy_id!)
// → 'https://example.com/privacy-policy'
// Hub public links
fetcher.documentUrl(docs.document_master_privacy_id!) // hub.goadopt.io/document/{id}
fetcher.dsarPortalUrl(requestsPageMasterId) // hub.goadopt.io/privacy-hub/{id}
// DSAR portal
const dsar = await discFetcher.getLinkedDsar()
// dsar.requestsPageMasterId, dsar.page (texts, requests, documents, style)
// Last linked DSAR master (even after unlink)
const masterId = await discFetcher.getLastLinkedDsarMasterId()
// Platform default texts for DSAR portal (pre-populate UI forms)
const [pt] = await fetcher.dsarDefaults('pt')
// pt.texts.title, pt.texts.requests.dataAccess, pt.texts.fields.name.nameDSAR portal management
// Create portal
await executor.executeForDisclaimer(disclaimerId, [
new RequestsPageProvisioner('dsar', {
name: 'Requests Portal',
dpoUserId: process.env.DPO_USER_ID!,
languages: { fallback: 'pt', languages: ['pt', 'en'] },
requests: [{ legislation: Legislation.LGPD, requests: Object.values(DsarRequestType) }],
style: { colors: { background: '#FFFFFF', button: '#00DD80' } }, // typed RequestsPageStyle
}),
])
// Disable (soft — reversible)
await client.disableDsarPage(disclaimerId)
// Re-enable
await client.linkRequestPage(disclaimerId, requestsPageMasterId)
// Permanently delete
await client.unlinkRequestPage(disclaimerId)
await client.deleteRequestsPage(requestsPageMasterId)Document management
// Link documents to disclaimer
await executor.executeForDisclaimer(disclaimerId, [
new PrivacyDocumentProvisioner('privacy', { language: 'pt', url: 'https://example.com/privacy' }),
new CookieDocumentProvisioner('cookies', { language: 'pt', url: 'https://example.com/cookies' }),
new TermsDocumentProvisioner('terms', { language: 'pt', url: 'https://example.com/terms' }),
])
// Update existing document (creates new version of same master)
await executor.executeForDisclaimer(disclaimerId, [
new PrivacyDocumentProvisioner('privacy-v2', {
language: 'pt',
url: 'https://example.com/privacy-v2',
masterDocumentId: existingMasterId, // ← targets existing master
}),
])
// Disable document toggle without deleting
await executor.executeForDisclaimer(disclaimerId, [
new PrivacyDocumentProvisioner('privacy-off', { language: 'pt', noDoc: true }),
])
// Permanently delete document master
await client.deleteDocument(docMasterId)Resume & rollback
// Fixed stateId enables resume on interrupted runs
const executor = new ResumableProvisioningExecutor({
client,
stateId: 'my-tenant-run', // omit to auto-generate (fresh run every time)
config: { rollbackOnError: true },
})
// Rollback a previous run by state ID
import { FreshProvisionerFactory } from '@adopt-tech/provisioning'
const { organizations } = await FreshProvisionerFactory.fromDefaultStorage('my-tenant-run')
await executor.rollback(organizations)Note: Only reuse a
stateIdto resume an interrupted run. For a new tenant, omit it or use a unique ID — reusing a completed state will skip provisioners that ran before.
Examples
Full, runnable examples are available at goadopt/adopt-provisioning-examples.
Provisioning
# Full partner onboarding: org, member, disclaimer, style, docs, scan
npx ts-node examples/partner-integration.ts --dry-run # preview tree
npx ts-node examples/partner-integration.ts # run
# Minimal disclaimer (all required documents)
npx ts-node examples/create-functional-disclaimer.tsBanner configuration
# Style — colors, position, fonts
npx ts-node examples/api-estilo.ts # apply
MODE=read npx ts-node examples/api-estilo.ts # read current
# Texts — title, body, button labels (per language + legislation)
npx ts-node examples/api-textos.ts
# Options — Google Consent Mode, hide after consent, TTL
npx ts-node examples/api-options.ts
MODE=read npx ts-node examples/api-options.ts
# Documents — privacy policy, cookie policy, terms of service
npx ts-node examples/api-documentos.ts # create/link via URL
MODE=read npx ts-node examples/api-documentos.ts # read current + hub links
MODE=update DOC_MASTER_ID=uuid npx ts-node examples/api-documentos.ts
MODE=nodoc npx ts-node examples/api-documentos.ts # disable togglesDSAR / Requests page (LGPD)
# Create/update portal with all 11 LGPD rights
npx ts-node examples/api-dsar.ts
# Read current portal config + public link
MODE=read npx ts-node examples/api-dsar.ts
# Platform default texts (pre-populate your UI forms)
MODE=defaults LANGUAGE=en npx ts-node examples/api-dsar.ts
# Full LGPD configuration — style, documents, all request types
npx ts-node examples/api-requisicoes.ts
# Disable / re-enable portal
MODE=unlink npx ts-node examples/api-dsar.ts
MODE=delete npx ts-node examples/api-dsar.tsMetrics & scanning
# Consent metrics for current month
npx ts-node examples/api-metricas.ts
MONTH=3 YEAR=2025 npx ts-node examples/api-metricas.ts
# Scan + recategorize tags
npx ts-node examples/scan-and-recategorize.ts
# TCF setup
npx ts-node examples/tcf-setup.tsLicense
MIT
