Invitations
Send, manage, and accept invitations to join organizations in HelloJohn.
Invitations
Invitations let you add external users to an organization via email. HelloJohn handles token generation, email delivery, and acceptance — including creating the user account if it doesn't exist yet.
How Invitations Work
1. Admin sends invitation → HelloJohn emails the invitee
2. Invitee clicks the link → Redirected to your app with ?token=...
3. Your app calls POST /v1/invitations/:token/accept
4. If user doesn't exist → Account is created automatically
5. User is added to the organization with the specified roleSending an Invitation
Via API
curl -X POST "https://api.hellojohn.dev/v1/organizations/org_01HABCDEF777666/invitations" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321" \
-H "Content-Type: application/json" \
-d '{
"email": "bob@example.com",
"role": "member",
"redirect_url": "https://app.example.com/accept-invitation"
}'Body:
| Field | Type | Required | Description |
|---|---|---|---|
email | string | ✅ | Invitee email address |
role | string | ✅ | member, admin, or owner |
redirect_url | string | ✅ | Where to send the invitee after clicking the link |
expires_in | string | — | Invitation expiry (default: 7d) |
Response: 201 Created
{
"id": "inv_01HABCDEF555999",
"organization_id": "org_01HABCDEF777666",
"email": "bob@example.com",
"role": "member",
"status": "pending",
"token": "inv_tok_abc123...",
"expires_at": "2024-01-22T10:00:00Z",
"created_at": "2024-01-15T10:00:00Z"
}Via SDK (React)
import { useOrganization } from "@hellojohn/react";
function InviteMember() {
const { invitations } = useOrganization();
const handleInvite = async (email: string) => {
await invitations.send({
email,
role: "member",
redirectUrl: "https://app.example.com/accept-invitation",
});
};
return (
<form onSubmit={(e) => {
e.preventDefault();
const email = new FormData(e.currentTarget).get("email") as string;
handleInvite(email);
}}>
<input name="email" type="email" placeholder="colleague@company.com" />
<button type="submit">Send Invitation</button>
</form>
);
}Listing Pending Invitations
curl "https://api.hellojohn.dev/v1/organizations/org_01HABCDEF777666/invitations" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321"Response:
{
"data": [
{
"id": "inv_01HABCDEF555999",
"email": "bob@example.com",
"role": "member",
"status": "pending",
"expires_at": "2024-01-22T10:00:00Z"
}
]
}Invitation statuses:
| Status | Description |
|---|---|
pending | Sent, not yet accepted |
accepted | Invitee joined the organization |
expired | Past the expiry date |
revoked | Manually cancelled |
Accepting an Invitation
Your app receives the invitation token via the redirect URL:
https://app.example.com/accept-invitation?token=inv_tok_abc123...Call the accept endpoint with the token:
curl -X POST "https://api.hellojohn.dev/v1/invitations/inv_tok_abc123.../accept" \
-H "X-Tenant-ID: tnt_01HABCDEF654321" \
-H "Content-Type: application/json" \
-d '{}'If the invitee is not yet signed in, include their credentials:
curl -X POST "https://api.hellojohn.dev/v1/invitations/inv_tok_abc123.../accept" \
-H "X-Tenant-ID: tnt_01HABCDEF654321" \
-H "Content-Type: application/json" \
-d '{
"name": "Bob Smith",
"password": "SecurePassword123!"
}'If the email matches an existing account, the user must sign in first. If not, a new account is created with the provided credentials.
Response: 200 OK
{
"user_id": "usr_01HABCDEF789012",
"organization_id": "org_01HABCDEF777666",
"role": "member",
"access_token": "eyJhbGciOiJFZERTQSJ9...",
"refresh_token": "rt_01HABCDEF..."
}Accept Flow with SDK
import { useHelloJohn } from "@hellojohn/react";
function AcceptInvitationPage() {
const { invitations } = useHelloJohn();
const token = new URLSearchParams(window.location.search).get("token");
const handleAccept = async () => {
const result = await invitations.accept(token!);
// result.organization — the org the user joined
window.location.href = "/dashboard";
};
return (
<div>
<h1>You've been invited!</h1>
<button onClick={handleAccept}>Accept Invitation</button>
</div>
);
}Revoking an Invitation
Cancel a pending invitation before it's accepted:
curl -X DELETE "https://api.hellojohn.dev/v1/organizations/org_01HABCDEF777666/invitations/inv_01HABCDEF555999" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321"Response: 204 No Content
Resending an Invitation
If the invitee didn't receive the email:
curl -X POST "https://api.hellojohn.dev/v1/organizations/org_01HABCDEF777666/invitations/inv_01HABCDEF555999/resend" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321"This sends a fresh email with a new token. The old token is invalidated.
Customizing the Invitation Email
See Email API — Templates to customize the invitation email template with your branding.