huudis.user.created.v1
Fires when a new Huudis user is provisioned — either by signup (someone created a Huudis account from any Forjio product's sign-in screen), by direct add from POST /v1/iam/users, or by an invite acceptance.
Subscribe if you back your own CRM, internal directory, or onboarding workflow with Huudis users.
Not yet emitted in v1. The event type is reserved in the catalog but the outbox publisher isn't wired for it currently. Subscribe defensively if you'd like to handle it when it ships — meanwhile, the audit log carries
auth.signupandaccount.member_addedentries with the same data.
When it fires
In every code path that creates a User row:
- Self-service signup at
/v1/auth/signup(someone clicks "Sign up" on a Forjio product). - Admin direct add at
/v1/iam/userswith an email that didn't previously have a Huudis user. - Social sign-in at
/v1/auth/google/callback(orapple/facebook) with an email Huudis hasn't seen. - Invite accept at
/v1/iam/invites/acceptwhen the invitee doesn't have a pre-existing Huudis user.
If the email already had a Huudis user (because they're a member of another workspace), huudis.user.created.v1 does not fire — only huudis.account.member_added.v1 does, because the user existed already.
Payload
{
"id": "evt_01KPG30TXM4N5Q8R1S4T6V8X0Y",
"type": "huudis.user.created.v1",
"createdAt": "2026-05-12T10:42:00.123Z",
"data": {
"id": "usr_01KPG40HMM…",
"email": "newbie@forjio.com",
"name": "Newbie",
"emailVerified": false,
"createdAt": "2026-05-12T10:42:00.123Z",
"signupSource": "plugipay-portal"
}
}
signupSource is the client_id of the OIDC client (or huudis-dashboard for direct signups). Useful for attributing acquisitions to specific products.
Handler examples
// Node
import { verifyWebhook } from '@forjio/huudis-node/webhooks';
app.post('/webhooks/huudis', async (req, res) => {
const event = verifyWebhook(
req.rawBody,
req.headers['huudis-signature'],
process.env.HUUDIS_WEBHOOK_SECRET
);
if (event.type === 'huudis.user.created.v1') {
const u = event.data;
await crm.contacts.upsert({
huudisId: u.id,
email: u.email,
name: u.name,
acquiredVia: u.signupSource,
});
}
res.status(200).end();
});
# Python
from huudis.webhooks import verify_webhook
@app.post("/webhooks/huudis")
def handle(req):
event = verify_webhook(req.raw_body, req.headers["huudis-signature"], WEBHOOK_SECRET)
if event["type"] == "huudis.user.created.v1":
u = event["data"]
crm.upsert(huudis_id=u["id"], email=u["email"], source=u.get("signupSource"))
return ("", 200)
// Go
event, err := huudis.VerifyWebhook(body, r.Header.Get("Huudis-Signature"), secret)
if err != nil { http.Error(w, "bad sig", 401); return }
if event.Type == "huudis.user.created.v1" {
var u huudis.User
_ = json.Unmarshal(event.Data, &u)
crm.Upsert(ctx, u.ID, u.Email, u.SignupSource)
}
What to do
- Upsert the user in your own directory keyed by
id(stable Huudis user ID). - Kick off welcome emails or in-app onboarding tours.
- Tag analytics with
signupSourceto attribute the acquisition.
Avoid:
- Granting workspace permissions here. Workspace membership is a separate event —
huudis.account.member_added.v1. - Sending the welcome email from your side if Huudis is already configured to. Huudis sends an "Email verification" email automatically; double-sending is annoying.
Common pitfalls
- Treating
emailas unique forever. Users can change their email at/v1/account/email-change. Useid, notemail, as your join key. - Assuming
emailVerified: true. A fresh user hasfalseuntil they click the verification link (or an admin force-verifies). Don't auto-trust the email until verification. - Double-processing across products. If two of your services both subscribe and both upsert into the same CRM, you'll race on the upsert. Pick one owning service.
Related events
huudis.account.member_added.v1— user joined a workspace.huudis.oidc.consent_granted.v1— user consented to a specific OIDC client.
Next
- Users resource — the admin API for workspace members.
- End users — the per-workspace view of consenting users.
- Webhook subscriptions — subscription management.