FR EN

Gate a feature in 5 minutes

The goal: before your code uses a piece of personal data for a purpose, it asks Agreely whether the person licensed that use, and respects the answer. The whole integration is one call.

1. Install and authenticate

npm install @agreely/sdk      # or: composer require agreely/sdk
export AGREELY_API_KEY=agr_live_xxx

A check-scoped key is all you need to gate features.

2. Find the cell

A consent cell is a category of data crossed with a purpose: Phone number x Billing. Use the exact human labels your catalog declares; the server normalizes them (trim, collapse whitespace, lowercase), so casing and spacing do not matter. Never normalize them yourself.

agreely catalog --json    # discover your declared (category, purpose) cells

3. Wrap the data use

import { Agreely } from "@agreely/sdk";

const agreely = new Agreely({ apiKey: process.env.AGREELY_API_KEY! });

async function chargeCard(customerId: string) {
  if (!(await agreely.check(customerId, "Phone number", "Billing"))) {
    return; // denied: do not use the phone number for billing
  }
  // allowed: proceed
}

check() returns a boolean and allow is the only true. A revoked, expired, erased, or never-granted cell is false.

4. Handle the reasons when it matters

For UX or logging, checkDetailed() tells you why:

const d = await agreely.checkDetailed(customerId, "Phone number", "Billing");
switch (d.status) {
  case "active":     /* allow */ break;
  case "necessity":  /* allow on the declared non-consent basis (d.basis) */ break;
  case "none":       /* never granted: ask for consent */ break;
  case "revoked":    /* withdrawn: stop, never re-ask automatically */ break;
  case "expired":    /* lapsed: re-ask */ break;
  case "relationship_ended":         /* P-39.1 s. 23 / A-2.1 s. 73 stop: do not re-ask automatically */ break;
  case "requires_depersonalization": /* depersonalized-only ground: never answer it by asking consent */ break;
  case "basis_not_in_regime":        /* the catalog cell needs a re-declaration */ break;
  default:                           /* an unknown status: treat it as a deny */ break;
}

Always branch on d.decision for the decision itself; the status is the why. An answer backed by a consent also carries d.tier (full, manual or verbal) and d.validUntil, the consent's end: your code decides which tier a use accepts, and validUntil is never a cache lifetime. The full vocabulary is in the API reference.

A deny is a normal 200. checkDetailed returns it; it only throws on auth, validation, rate-limit, or outage errors.

5. Know the failure mode

The check is fail-closed. If Agreely is unreachable, check() returns false (deny) and checkDetailed() throws AgreelyUnavailableError. That is the safe default: an outage never silently grants access. If a specific, low-risk category genuinely needs to keep working through an outage, see handling outages for the deliberate, audited fail-open exception.

Next