Wocha Docs

Organisation invites

Invite members by email with a first-password hosted flow, optional Wocha mail, or your own email template

Invite a person by email. Wocha finds or creates their identity, adds them to the organisation, and issues a first-password link (not a “forgot password” email). You can let Wocha send a branded “You’ve been invited” message, or skip Wocha mail and put the action URL in your own product email.

Do not overload POST /users/:id/recovery as the invite API, and do not send invitees to a product-owned MFA enrolment page. TOTP on this path is hosted Wocha MFA.


When to use which API

GoalAPIEmail
Invite someone into an organisationPOST /v1/organisations/{id}/invitesWocha invite_link template, or your own mail with action_url
Reset password for an existing userPOST /v1/users/{id}/recoveryWocha recovery_link template, or silent send: false
Add an identity that already existsPOST /v1/organisations/{id}/membersNone — membership only

Required scope: organisations:write for invites; users:write for recovery.


Flow

sequenceDiagram
    participant App as Your product
    participant API as Customer API
    participant Mail as Email
    participant User as Invitee
    participant Auth as id.wocha.ai
 
    App->>API: POST /organisations/{id}/invites
    API->>API: Find or create identity, add member
    API->>API: Issue first-password or login URL
    alt send true (default)
        API->>Mail: Branded invite_link email
        Mail->>User: Open action_url
    else send false
        API-->>App: 201 with action_url
        App->>Mail: Your own invite email
        Mail->>User: Open action_url
    end
    User->>Auth: /invite (new user) or /login (has password)
    Auth->>User: Create password
    opt require_mfa true (default)
        Auth->>User: Enrol TOTP
    end
    Auth->>App: Redirect to return_to
  1. Your backend calls the invite endpoint with the person’s email and role.
  2. Wocha creates the identity if needed and writes organisation membership immediately.
  3. If they have no password, action_url is a hosted /invite magic link (24 hours). If they already have a password, action_url is hosted /login with the org and return_to attached.
  4. On /invite they set a password. When require_mfa is true (the default), they enrol TOTP before leaving Auth.
  5. Auth then honours return_to — this should be your product OAuth start URL, not an unauthenticated org home page.

Invite with Wocha email

send defaults to true. Wocha sends the invite_link template via Resend.

import { WochaClient } from "@wocha/sdk";
 
const wocha = new WochaClient({
  tenant: "acme",
  apiKey: process.env.WOCHA_API_KEY!,
});
 
const invite = await wocha.organisations.invite(orgId, {
  email: "alex@example.com",
  role: "member",
  name: { first: "Alex", last: "Cole" },
  return_to: "https://app.example.com/api/auth/login",
  require_mfa: true,
  inviter_name: "Jordan from Acme",
});
 
// invite.action_url — hosted create-password (or sign-in) URL
// invite.emailed — true when Resend accepted the message

If Resend fails after membership and the link already exist, the API still returns 201 with action_url and emailed: false. Retry mail yourself from that URL rather than treating it as a failed invite.

Full field list: Organisation invite API.


Invite with your own email (send: false)

Products that already send invite copy must pass send: false. Wocha does not email. Put action_url in your template.

const invite = await wocha.organisations.invite(orgId, {
  email: "alex@example.com",
  role: "member",
  name: { first: "Alex", last: "Cole" },
  return_to: "https://portal.example.com/api/auth/login",
  send: false,
  require_mfa: true,
});
 
await sendProductInviteEmail({
  to: "alex@example.com",
  actionUrl: invite.action_url, // https://id.wocha.ai/invite?flow=…&token=…
});
invite = client.organisations.invite(
    org_id,
    email="alex@example.com",
    role="member",
    return_to="https://portal.example.com/api/auth/login",
    send=False,
)
# invite["action_url"]  — put this in your own invite email
# invite["emailed"]     — False

Never send two emails. If send is omitted, Wocha mails by default.


Provider-branded invites

If your product sells into other organisations (a vendor inviting staff into a customer's organisation), pass branding_org_id so the invite email, the hosted invite page and the invitee's later auth mail carry your brand while membership stays on the customer's organisation:

await wocha.organisations.invite(customerOrgId, {
  email: "alex@example.com",
  role: "member",
  return_to: "https://www.campaigncoach.au/api/auth/login",
  branding_org_id: "campaign-coach", // id or slug of your own organisation
});

Both organisations must belong to your tenant; anything else is a 400. The branding organisation supplies the invite_link template, logo and colours, custom From sender and the org_id branding hint on the hosted pages. The invited organisation still supplies {{ .OrganisationName }}. The response includes branding_org_id when it differs from the path organisation.

For a standing default, set metadata.email_branding_org on the customer's organisation instead (Console or PATCH /organisations/{id}); every invite into it, and every courier email to its members, then uses that brand.

Hosted invitee experience

SituationWhere they landAfter password
New identity, no passwordhttps://id.wocha.ai/invite?flow=&token=TOTP enrolment when require_mfa is true, then return_to
Existing identity with a passwordhttps://id.wocha.ai/login?org_id=&return_to=Normal sign-in, then return_to

The invite page sets cookies (auth_invite_pending, auth_flow_return_to, org branding) then completes the magic link on the Auth origin so the session cookie is scoped to id.wocha.ai.

TOTP on this path is Wocha MFA. Complete it on hosted Auth; do not send the invitee to a product-owned MFA settings screen.


return_to

return_to must be https on an allowlisted origin. After the user has a session, send them to the URL that starts OAuth in your app (BFF /api/auth/login, /auth/login, and so on). Hydra can then skip a second login and land them in the product.

Do not use:

  • The organisation home page if it is not an OAuth entrypoint
  • Product-owned MFA enrolment URLs

If return_to is missing or not allowlisted, the API returns 400 with return_to must be an https URL on an allowlisted application origin. Ask Wocha to add your production and staging origins to RECOVERY_ALLOWED_RETURN_URLS.


Password recovery (not invites)

Use recovery when the identity already exists and you only need a set-password link.

const result = await wocha.users.sendRecovery(userId, {
  return_to: "https://app.example.com/api/auth/login",
  send: false, // skip Wocha mail; result.recovery_link is the hosted URL
});

Hosted recovery links go to https://id.wocha.ai/recovery?flow=&token=&return_to=&org_id=. Auth persists return_to and org branding in cookies before completing the token, same pattern as invites.

See User recovery API.


Webhooks and audit

EventWhen
user.createdInvite created a new identity (source: customer_api.invite)
organisation.member_addedMembership written (invited: true on this path)
Audit customer_api.org.invitedInvite succeeded (including when Wocha mail was skipped or failed)

Subscribe to organisation.member_added if your product keeps a local membership table. See the webhook event catalogue.


Errors

StatusCodeMeaning
400validation_errorMissing email, invalid role, or return_to not allowlisted
401unauthorizedMissing or invalid API key
403forbiddenToken lacks organisations:write
404not_foundOrganisation (or recovery identity) not found
409conflictEmail already belongs to another tenant
429quota_exceededUser / MAU quota when creating a new identity

Membership is written before mail. A mail-provider outage does not roll back the invite.


See also

On this page