Consent banner
The <asiri-consent> component is part of the @asiri-ng/elements family — a collection of dependency-free web components for Asiri. It renders a cookie-consent
banner, captures the visitor’s choices, and writes a signed record to your Asiri consent ledger —
all without any backend code on your side.
Prerequisites
- You must have a published consent notice in your workspace. Go to Compliance → Consent
notices, publish the notice, then open Developer → Embeds to find its notice ID.
Alternatively, call
GET /v1/public/consent/{noticeId}— if the notice exists and is published, it returns the notice details; a 404 means it hasn’t been published yet. - The notice’s domain must match the site you embed on. The API returns
403 consent_origin_not_allowedfor records submitted from any other origin.
Quick start — no build required
Paste two lines before your closing </body> tag:
<script
async
src="https://cdn.jsdelivr.net/npm/@asiri-ng/elements@0.1.0/dist/consent.global.js"
integrity="sha384-REPLACE_WITH_SRI_HASH"
crossorigin="anonymous"
></script>
<asiri-consent notice-id="YOUR_NOTICE_ID"></asiri-consent>Replace YOUR_NOTICE_ID with the ID from Developer → Embeds. Replace
REPLACE_WITH_SRI_HASH with the Subresource Integrity hash published in the
release notes for the version you pin — each
release publishes the sha384- digest for its CDN bundle. Loading an external script without
SRI exposes your site to CDN-level compromise, so always pin both the version and its hash.
The banner appears on first visit and disappears once the visitor makes a choice. Returning visitors who have already decided see no banner.
Attributes
| Attribute | Required | Default | Description |
|---|---|---|---|
notice-id | Yes | — | Your published consent notice ID. |
api-base | No | https://api.asiri.ng | Override the API base URL (e.g. staging). |
Install via npm
For React, Vue, Svelte, or any bundler-based project:
pnpm add @asiri-ng/elementsImport the consent component once — it self-registers the asiri-consent custom element and sets up
window.AsiriConsent:
import '@asiri-ng/elements/consent';Then place the element anywhere in your markup:
<asiri-consent notice-id="YOUR_NOTICE_ID"></asiri-consent>React
The element is a standard custom element and works directly in JSX. TypeScript users: cast it
with as any or add a global JSX namespace extension until @asiri-ng/elements ships React types.
import '@asiri-ng/elements/consent';
export function App() {
const noticeId = process.env.NEXT_PUBLIC_ASIRI_NOTICE_ID!;
return (
<>
{/* your app */}
<asiri-consent notice-id={noticeId} />
</>
);
}WordPress / Google Tag Manager
Paste the script-tag version into a Custom HTML block (WordPress block editor) or a Custom HTML tag (GTM):
<script
async
src="https://cdn.jsdelivr.net/npm/@asiri-ng/elements@0.1.0/dist/consent.global.js"
integrity="sha384-REPLACE_WITH_SRI_HASH"
crossorigin="anonymous"
></script>
<asiri-consent notice-id="YOUR_NOTICE_ID"></asiri-consent>Use the SRI hash from the release notes .
Gate your own scripts on consent
Use window.AsiriConsent to load third-party scripts only after the visitor decides.
window.AsiriConsent.onDecision((state) => {
if (state.purposes.analytics) {
// initialise your analytics library here
}
if (state.purposes.marketing) {
// load ad pixels here
}
});onDecision fires immediately if a stored decision already exists (e.g. returning visitor), so
your scripts load without waiting for banner interaction. The callback receives a ConsentState
object:
interface ConsentState {
decided: boolean; // always true inside the callback
purposes: Record<string, boolean>; // purpose key → granted
}Purpose keys match the keys you defined on the notice (for example strictly_necessary,
analytics, marketing). Required purposes are always true.
Re-open the preferences panel
Add a “Cookie settings” link in your footer and call window.AsiriConsent.open():
document.querySelector('#cookie-settings').addEventListener('click', () => {
window.AsiriConsent.open();
});Read the current state synchronously
const state = window.AsiriConsent.get();
// state is null until the visitor has decided, or ConsentState after.DOM event
The element also dispatches a asiri-consent event on document whenever a decision is
recorded or loaded from storage. Listen for it if you prefer events over callbacks:
document.addEventListener('asiri-consent', (event) => {
const { decided, purposes } = event.detail;
// same shape as ConsentState
});Full window.AsiriConsent API
| Method | Returns | Description |
|---|---|---|
get() | ConsentState | null | Current decision, or null before any decision. |
onDecision(cb) | () => void | Subscribe to decisions; fires immediately if already decided. Returns an unsubscribe function. |
open() | void | Re-open the preferences panel. |
Server-side consent (advanced)
Record or read consent from your backend using an API key and the official SDKs. This is useful when you collect consent outside the browser — for example during a phone call, in a mobile app, or through a paper form.
Requires a secret API key with consent:write / consent:read scope. See Getting Started →
API Keys.
TypeScript / Node
Install the SDK: SDKs → TypeScript.
import { createAsiriClient } from '@asiri-ng/sdk-ts';
const client = createAsiriClient({
baseUrl: 'https://api.asiri.ng',
apiKey: process.env.ASIRI_API_KEY,
});
// Record a consent decision
const record = await client.consent.record('NOTICE_ID', {
subjectId: 'user_123',
purposes: { strictly_necessary: true, analytics: true, marketing: false },
method: 'phone',
});
// List records for a notice (paginated)
const page = await client.consent.listRecords('NOTICE_ID', { limit: 50 });
// Fetch a single record
const single = await client.consent.getRecord('NOTICE_ID', record.id);Python
Install the SDK: SDKs → Python.
from asiri import AsiriClient
client = AsiriClient(
base_url="https://api.asiri.ng",
api_key=os.environ["ASIRI_API_KEY"],
)
# Record a consent decision
record = client.consent.record(
"NOTICE_ID",
"user_123",
purposes={"strictly_necessary": True, "analytics": True, "marketing": False},
method="phone",
)
# List records for a notice (paginated)
page = client.consent.list_records("NOTICE_ID", limit=50)
# Fetch a single record
single = client.consent.get_record("NOTICE_ID", record["id"])Public REST API reference
The two endpoints below are unauthenticated and CORS-open — any origin can call them from a browser. They are not included in the auto-generated API reference because they use a separate public authentication model.
Get a published notice
Fetch the notice definition, including its purposes. Returns 404 if the notice doesn’t exist
or hasn’t been published.
GET https://api.asiri.ng/v1/public/consent/{noticeId}Response — 200
{
"data": {
"id": "ntc_01jv3q4e5f0000000000000000",
"name": "Main website",
"controllerName": "Acme Ltd",
"language": "en",
"domain": "acme.com",
"purposes": [
{
"key": "strictly_necessary",
"label": "Strictly necessary",
"description": "Required for the site to function. Cannot be disabled.",
"required": true
},
{
"key": "analytics",
"label": "Analytics",
"description": "Helps us understand how visitors use the site.",
"required": false
},
{
"key": "marketing",
"label": "Marketing",
"description": "Used to deliver relevant advertising.",
"required": false
}
]
}
}Error — 404 — notice not found or not published.
Submit a consent record
Record a visitor’s consent decision. The web component calls this endpoint automatically; call it directly only if you’re building a custom consent UI.
POST https://api.asiri.ng/v1/public/consent/{noticeId}/recordsRequest body
{
"subjectId": "anon-a1b2c3d4",
"purposes": {
"strictly_necessary": true,
"analytics": true,
"marketing": false
},
"method": "banner",
"capturedAt": "2026-06-23T09:15:00.000Z"
}| Field | Type | Required | Description |
|---|---|---|---|
subjectId | string | Yes | Pseudonymous identifier for the visitor (e.g. a UUID from local storage). |
purposes | { [key: string]: boolean } | Yes | One entry per purpose key from the notice. |
method | string | No | How consent was captured — e.g. "banner", "phone". |
capturedAt | ISO 8601 string | No | When consent was captured. Defaults to server time. |
Response — 201 — record created.
Error — 403 consent_origin_not_allowed — the request Origin does not match the notice’s
configured domain. Verify that the notice domain matches the site you’re embedding on.
Privacy notes
- The request
Originis validated against the notice domain but not stored. - Raw IP addresses and user-agent strings are never persisted — only salted hashes.
- The endpoint is rate-limited per notice to prevent abuse.