Predict.aiDocs
Workspaces

Invitations

Invite someone to an organization by email; verify the token, then accept or decline.

Joining an organization is a three-step handshake:

Admin              POST /v1/organization/{org_id}/invite        → email with a token
Recipient          GET  /v1/organization/verify-invitation      → invitation_id + details
Recipient          POST /v1/organization/invitations/{id}/accept  (or /decline)

The email carries a one-time token; verifying it returns the invitation ID used to accept or decline. Invitations expire after 30 days.

Send an invitation

Requires the organization.members.invite permission — organization admins (and the owner):

FieldMeaning
email requiredWho to invite.
role optionaladmin, billing, member, or viewer. Defaults to the organization's default_member_role policy (member unless changed).
curl -X POST "$API_BASE/v1/organization/$ORG_ID/invite" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "email": "[email protected]", "role": "member" }'
{
  "data": {
    "message": "Invitation sent successfully",
    "invitation_id": "inv_9c2e4f6a-1b83-4d57-a0e9-6f24c81d3b90",
    "email": "[email protected]",
    "role": "member",
    "expires_at": "2026-08-14T12:00:00+00:00"
  }
}

If the address has no predictAI account yet, one is created on the Free plan so the invitation has somewhere to land — the recipient claims it when they first sign in. The invitation email is always delivered, regardless of the recipient's notification preferences.

Errors

StatusWhyExample message
400Malformed email"Invalid email address"
400Unknown role"Role must be one of: admin, billing, member, viewer"
400Already in the organization"User is already a member"
403Not an admin"Insufficient permissions"
403Email outside allowed_email_domains"This organization only allows members from: acme.dev"
403No free seats on the plan"All 5 seats on this organization's plan are in use. Add seats to your subscription (or remove a member) before inviting more people."

The seat-cap error comes back structured:

{
  "error": {
    "code": "seat_limit_reached",
    "message": "All 5 seats on this organization's plan are in use. Add seats to your subscription (or remove a member) before inviting more people.",
    "details": { }
  },
  "status": "All 5 seats on this organization's plan are in use. Add seats to your subscription (or remove a member) before inviting more people."
}

Verify an invitation

The recipient's email link carries a token — this is the one route in the section that needs no authentication, so it works before the recipient has signed in:

curl "$API_BASE/v1/organization/verify-invitation?token=$INVITATION_TOKEN"
{
  "data": {
    "valid": true,
    "invitation_id": "inv_9c2e4f6a-1b83-4d57-a0e9-6f24c81d3b90",
    "organization_name": "Acme Analytics",
    "role": "member",
    "email": "[email protected]"
  }
}
ParameterMeaning
token requiredThe one-time token from the invitation email.

Keep the invitation_id — it's what accept and decline take.

Errors

StatusWhyExample message
400No token in the query string"Token is required"
401Token is invalid, expired, or already used"Invalid, expired, or already used invitation"
{
  "error": {
    "code": "invalid_token",
    "message": "Invalid, expired, or already used invitation",
    "details": { }
  },
  "status": "Invalid, expired, or already used invitation"
}

Accept or decline

Both calls authenticate as the recipient and take the invitation_id:

curl -X POST "$API_BASE/v1/organization/invitations/$INVITATION_ID/accept" \
  -H "Authorization: Bearer $TOKEN"
{ "data": { "message": "Invitation accepted successfully" } }

Accepting adds you to the organization with the invited role, effective immediately.

curl -X POST "$API_BASE/v1/organization/invitations/$INVITATION_ID/decline" \
  -H "Authorization: Bearer $TOKEN"
{ "data": { "message": "Invitation declined successfully" } }

Errors

StatusWhyExample message
400Invitation not found, not pending, or the organization's seats filled up since the invite was sent"Failed to accept invitation or invitation not found"
400(decline) Invitation not found or not pending"Failed to decline invitation or invitation not found"

List an organization's invitations

Any member can list invitations:

ParameterMeaning
status optionalpending (default), accepted, declined, or expired.
curl "$API_BASE/v1/organization/$ORG_ID/invitations?status=pending" \
  -H "Authorization: Bearer $TOKEN"
{
  "data": {
    "invitations": [
      {
        "uid": "inv_9c2e4f6a-1b83-4d57-a0e9-6f24c81d3b90",
        "email": "[email protected]",
        "role": "member",
        "status": "pending",
        "created_at": "2026-07-15T12:00:00+00:00",
        "expires_at": "2026-08-14T12:00:00+00:00",
        "inviter_email": "[email protected]",
        "inviter_name": "Ada Norberg"
      }
    ],
    "count": 1
  }
}

Revoke an invitation

Requires organization.members.invite. Only still-pending invitations can be revoked; the emailed link stops working immediately:

curl -X DELETE "$API_BASE/v1/organization/$ORG_ID/invitations/$INVITATION_ID" \
  -H "Authorization: Bearer $TOKEN"
{ "data": { "message": "Invitation revoked", "invitation_id": "inv_…" } }

Errors

StatusWhyExample message
403Not allowed to manage invites"Insufficient permissions"
404Invitation unknown or no longer pending"Invitation not found or no longer pending"

On this page