Entity Enrollment (Keys & Padlock)
Entity Enrollment lets you register devices, kiosks, or non-human entities with passkeys using single-use tickets. Your backend (or a trusted issuer) creates a ticket; the client uses the TryMellon SDK to complete the WebAuthn ceremony and bind a passkey to that entity. No passwords, no long-lived shared secrets—just time-limited tickets and cryptographic binding.
Use this when you need per-device or per-entity credentials (IoT, fleet devices, kiosks, or AI agents that act as a fixed “device” in the field).
1. What you get
| Capability | Description |
|---|---|
| Single-use tickets | Each ticket is consumed once when enrollment finishes. No reuse. |
| Context binding | A context hash ties the ticket to the exact client context; tampering or replay on another origin fails. |
| Config per app | In the dashboard: enable Entity Enrollment per application, set ticket TTL (30–300 s) and max active tickets (1–100). |
| SDK flow | client.enroll({ ticketId }) runs the WebAuthn registration; your backend issues tickets via the API. |
2. Flow (high level)
- Issue a ticket — Your backend (or a secure process) calls
POST /v1/enrollment/ticketswithentity_idand optionalttl_seconds. You get aticket_idand optionalcontext_hash(if your backend generates it; otherwise the SDK can derive it). - Client enrolls — The device or kiosk loads your app, receives the
ticket_id(e.g. from URL, QR, or provisioning), and callsclient.enroll({ ticketId }). The SDK performs the WebAuthn registration and sends the credential to TryMellon. - Finish — TryMellon consumes the ticket and binds the new passkey to the
entity_id. Later, that entity can authenticate withclient.signIn()using the same credentials.
3. Where to configure
- Dashboard: Open your app → Entity Enrollment section.
- Toggle: Enable or disable Entity Enrollment for that application.
- TTL: How long a ticket stays valid (30–300 seconds). After that, the ticket expires and cannot be used.
- Max active tickets: Maximum number of tickets that can be active at once for this app (1–100). Prevents unbounded issuance.
Backend details (API contracts, auth, rate limits) are documented in the API reference and in the internal design docs (docs/design/keysAndpadlock/).
4. Limits and plans
Entity Enrollment is a Growth+ feature (Growth, Scale, Enterprise). Limits are applied per application (TTL and max active tickets). There is no separate per-tenant enrollment quota in the current plans; capacity is effectively governed by your plan’s user and app limits.
4. End-to-end example
Backend — Issue a ticket
// Your backend (Node.js / any server)
const response = await fetch('https://api.trymellonauth.com/v1/enrollment/tickets', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_CLIENT_SECRET',
'Content-Type': 'application/json',
},
body: JSON.stringify({
entity_id: 'kiosk-store-42', // your identifier for this device/entity
ttl_seconds: 120, // ticket valid for 2 minutes
context_hash: contextHashFromClient, // optional: bind to a specific client context
}),
});
const { data } = await response.json();
// data = { ticket_id: 'tkt_xxx', expires_at: '...' }
// Send data.ticket_id to the device (via QR, URL param, or provisioning payload)
Frontend — Enroll the device
import { TryMellon } from '@trymellon/js';
const clientResult = TryMellon.create({
appId: 'YOUR_APP_ID',
publishableKey: 'cli_xxxx',
});
if (!clientResult.ok) throw clientResult.error;
const client = clientResult.value;
// Get context hash to send to backend before issuing the ticket (optional but recommended)
const contextHash = client.getContextHash();
// After receiving the ticket_id from your backend (e.g. from URL param or provisioning):
const ticketId = new URLSearchParams(window.location.search).get('ticket_id') ?? '';
const result = await client.enroll({ ticketId });
if (result.ok) {
const { sessionToken, credentialId, userId, entityId } = result.value;
// Send sessionToken to your backend to validate and create a device session
await fetch('/api/device/session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sessionToken }),
});
} else {
// Handle: TICKET_NOT_FOUND, TICKET_EXPIRED, TICKET_ALREADY_USED
console.error(result.error.code, result.error.message);
}
5. Related docs
- Getting Started — Install the SDK and configure your app.
- API Reference —
client.enroll(),getContextHash(), and enrollment options. - Dashboard: create an app, enable Entity Enrollment, and copy the App ID and publishable key for your integration.