PII-safe identifiers
Allowly receipts are durable. Anything you put in user_id, agent_id, resource, or context can become part of a signed audit record.
Use your app's opaque internal user ID when you have one. If your app needs to derive a stable ID from email, use the SDK helper so the raw email is hashed locally before any request reaches Allowly.
Python
from allowly.identifiers import from_email
allowly_user_id = from_email(
user.email,
pepper=os.environ["ALLOWLY_PII_PEPPER"],
)
authorization = await allowly.authorizations.create(
user_id=allowly_user_id,
agent_id="sales-copilot",
scopes=["email.send"],
expires_at="2026-12-31T00:00:00Z",
)
TypeScript
import { identifiers } from "@allowly/sdk";
const allowlyUserId = identifiers.fromEmail(user.email, {
pepper: process.env.ALLOWLY_PII_PEPPER!,
});
const authorization = await allowly.authorizations.create({
userId: allowlyUserId,
agentId: "sales-copilot",
scopes: ["email.send"],
expiresAt: "2026-12-31T00:00:00.000Z",
});
The result is a string like:
email_hmac:v1:joGNnOl733jVwFo68Eh9yBii-N5CkEOwKDyTFZTKpVI
Rules
- The raw email and pepper stay in your app. Allowly never receives them.
- There is no Allowly API endpoint that accepts a raw email to mask or hash it.
- The pepper is a permanent customer-held secret. Do not derive it from an Allowly API key.
- Back up the pepper like signing key material. If you lose or change it, derived IDs change and existing authorizations may be orphaned.
- Email normalization is intentionally minimal and frozen: trim whitespace and lowercase only.
- No provider-specific normalization is applied. Gmail dot stripping, plus-address handling, and domain alias rules are not used.
- Allowly treats the full
user_idas opaque. It does not parseemail_hmac, validate the version, or infer that the subject was an email.
Raw email is still accepted as a user_id if you deliberately want it in your own receipts, but it is not recommended.