HonkIO Auth
Auth webhook events
Nineteen events for users, sessions, organizations and security, delivered by your existing HonkIO webhooks.
Subscribing
HonkIO Auth events use your account’s existing webhooks: the same signing with X-HonkIO-Signature and X-HonkIO-Timestamp (Signing), the same retries and dead-letter handling (Retries). Subscribe to them like any other event:
curl -X POST https://api.honkio.ca/v1/webhooks \
-H "Authorization: Bearer mk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://app.acme.ca/hooks/honkio",
"events": ["user.created", "user.deleted", "session.revoked", "membership.deleted"]
}'Each event’s data carries app_id, and livemode follows the app’s mode. Payloads never contain codes, tokens, keys or secrets. The API key that creates a webhook for these events, or changes its URL, also needs read access to their data: auth_users for user, session, sign-in and sign-up events, auth_organizations for organization and membership events, and auth_apps for app events. A key without it gets 403 FORBIDDEN, and sees those events’ dead letters without their payload.
User events
user.created
A user signed up, or accepted an invitation as a new user.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"user": {
"id": "usr_clxxxuserxxxxxxxxxxxxxx",
"email": "ada@example.com",
"email_verified": true,
"phone": null,
"phone_verified": false,
"name": "Ada Lovelace",
"blocked": false,
"created_at": "2026-11-02T15:04:05.000Z",
"last_sign_in_at": "2026-11-02T15:04:05.000Z"
}
}user.updated
A user’s profile or sign-in methods changed; changes names the fields.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"user": {
"id": "usr_clxxxuserxxxxxxxxxxxxxx",
"email": "ada@example.com",
"email_verified": true,
"phone": "+1416XXXXXXX",
"phone_verified": true,
"name": "Ada Lovelace",
"blocked": false,
"created_at": "2026-11-02T15:04:05.000Z",
"last_sign_in_at": "2026-11-02T15:04:05.000Z"
},
"changes": [
"phone"
]
}user.deleted
A user was deleted. Only the ID remains.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"user": {
"id": "usr_clxxxuserxxxxxxxxxxxxxx"
}
}user.blocked
A user was blocked, with your reason.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"user": {
"id": "usr_clxxxuserxxxxxxxxxxxxxx",
"email": "ada@example.com",
"email_verified": true,
"phone": null,
"phone_verified": false,
"name": "Ada Lovelace",
"blocked": true,
"created_at": "2026-11-02T15:04:05.000Z",
"last_sign_in_at": "2026-11-02T15:04:05.000Z"
},
"reason": "Chargeback fraud"
}user.locked
An existing user’s identifier was locked after too many wrong codes, until locked_until. Links and passkeys still work.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"user_id": "usr_clxxxuserxxxxxxxxxxxxxx",
"locked_until": "2026-11-02T16:04:05.000Z"
}Session events
session.created
A user signed in.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"session": {
"id": "ses_xxxxxxxxxxxxxxxxxxxxxx",
"user_id": "usr_clxxxuserxxxxxxxxxxxxxx",
"amr": [
"otp"
],
"organization_id": null,
"created_at": "2026-11-02T15:04:05.000Z",
"expires_at": "2026-12-02T15:04:05.000Z"
}
}session.revoked
A session ended before it expired.
reason is one of:
| Reason | Meaning |
|---|---|
| logout | the user signed out |
| refresh_reuse | an old refresh token was reused, so the whole session was revoked |
| revoked | your back end revoked the refresh token at /revoke |
| refresh_refused | a refresh was refused, for example after removal from the organization |
| not_me | the user clicked “This wasn’t me” on a new-device alert |
| api | you revoked it through the API or the dashboard |
| user_blocked | the user was blocked |
| method_changed | a sign-in method was added, changed or removed |
| staff | HonkIO staff ended it |
| hostname_changed | the app moved to a new hostname, so its issuer changed |
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"session": {
"id": "ses_xxxxxxxxxxxxxxxxxxxxxx",
"user_id": "usr_clxxxuserxxxxxxxxxxxxxx",
"amr": [
"otp"
],
"organization_id": null,
"created_at": "2026-11-02T15:04:05.000Z",
"expires_at": "2026-12-02T15:04:05.000Z"
},
"reason": "logout"
}Organization events
organization.created
An organization was created.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"organization": {
"id": "org_clxxxorgxxxxxxxxxxxxxxx",
"name": "Northwind",
"slug": "northwind",
"created_at": "2026-11-02T15:04:05.000Z"
}
}organization.updated
An organization was renamed or its settings changed.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"organization": {
"id": "org_clxxxorgxxxxxxxxxxxxxxx",
"name": "Northwind Traders",
"slug": "northwind",
"created_at": "2026-11-02T15:04:05.000Z"
}
}organization.deleted
An organization was deleted.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"organization": {
"id": "org_clxxxorgxxxxxxxxxxxxxxx",
"name": "Northwind",
"slug": "northwind",
"created_at": "2026-11-02T15:04:05.000Z"
}
}membership.created
A user joined an organization.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"membership": {
"organization_id": "org_clxxxorgxxxxxxxxxxxxxxx",
"user_id": "usr_clxxxuserxxxxxxxxxxxxxx",
"role": "member"
}
}membership.updated
A member’s role changed.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"membership": {
"organization_id": "org_clxxxorgxxxxxxxxxxxxxxx",
"user_id": "usr_clxxxuserxxxxxxxxxxxxxx",
"role": "admin"
}
}membership.deleted
A user left or was removed from an organization. Sent at once; their next refresh in it fails.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"membership": {
"organization_id": "org_clxxxorgxxxxxxxxxxxxxxx",
"user_id": "usr_clxxxuserxxxxxxxxxxxxxx",
"role": "member"
}
}Security events
signin.suspicious
A risky sign-in was challenged or flagged.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"user_id": "usr_clxxxuserxxxxxxxxxxxxxx",
"risk_level": "high",
"reasons": [
"datacenter_network",
"new_device"
],
"new_device": true,
"flagged": true
}signup.blocked
Sign-ups were blocked in the last minute: a count by reason, sent at most once a minute.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"window_start": "2026-11-02T15:04:00.000Z",
"window_end": "2026-11-02T15:05:00.000Z",
"count": 42,
"reasons": {
"throwaway_email": 30,
"risk": 12
}
}app.attack_mode.started
The app went into attack mode, automatically or by hand.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"trigger": "auto",
"started_at": "2026-11-02T15:04:05.000Z"
}app.attack_mode.ended
Attack mode ended.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"trigger": "auto",
"started_at": "2026-11-02T15:04:05.000Z",
"ended_at": "2026-11-02T17:30:00.000Z"
}app.updated
The app moved to a new hostname (PATCH with hostname): changes is ["hostname"], with the new hostname and issuer, the previous_hostname and previous_issuer, how many sessions ended and how many passkeys were retired.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"changes": [
"hostname"
],
"hostname": "auth.acme.ca",
"issuer": "https://auth.acme.ca",
"previous_hostname": "login.acme.ca",
"previous_issuer": "https://login.acme.ca",
"sessions_revoked": 42,
"passkeys_retired": 0
}sms.spend_limit_reached
The daily text spending limit was reached and text sign-in paused.
{
"app_id": "clxxxappxxxxxxxxxxxxxxxxx",
"limit_cents": 500,
"spent_cents": 503,
"day": "2026-11-02"
}
HonkIO