Skip to content

Invite Member

POST
/roles/invite

Invite one person into the organisation, at a role below administrator.

status in the response is the app’s own: invited when an invitation email went out to someone with no graph8 account, added when the person already had one and now holds the role immediately. Re-inviting the same address is safe - the pending invitation is UPDATED to the new role rather than duplicated - so a reconciliation loop may call this once per row.

ADMINISTRATOR ROLES ARE REFUSED HERE, AND THAT IS WHY THIS ROUTE CAN EXIST AT ALL. The module docstring withholds every route that can end with a person holding PropelAuth Owner or Admin. The reason is specific: build_api_principal can answer AS an org administrator, and the app’s _assert_can_grant_permissions bypasses its non-escalation check for a privileged org role - so a bare proxy of the app handler would let a credential mint an administrator. Capping the role here removes exactly that, and leaves the capability that was actually asked for: admitting members and project roles in bulk from a script, a workflow or an agent. Granting administration stays a decision for a person, in the product.

422 when neither or both of role_id/role are given; 404 when the role is not this organisation’s; 403 when the role is administrator-equivalent, or when the app refuses the grant because the key’s own permissions do not cover the role’s.

Authorizations

Parameters

Header Parameters

X-Target-Org-Id
Any of:
string

Request Body required

Invite one person into the organisation. Mirrors roles.InviteUserRequest with ONE addition: the role may be named instead of identified by UUID.

The app model takes role_id: UUID only, because its caller is a role picker that already holds the id. An API caller holds a NAME - “Engineer”, “Member” - and making it fetch GET /roles first to translate is a round trip this contract can absorb. So exactly one of role_id or role is required; role is resolved against this organisation’s own roles by slug first, then by case-insensitive display name.

role is deliberately NOT defaulted to “Member”. An absent role on an invitation is a caller mistake, and silently picking the fallback is how someone ends up in an organisation at a permission level nobody chose.

object
email
required

Email address to invite. Matched case-insensitively against existing members and invitations.

string
>= 3 characters <= 255 characters
list_ids
Any of:
Array<integer>
role
Any of:
string
>= 1 characters <= 100 characters
role_id
Any of:
string format: uuid

Responses

201

Successful Response

object
data
required

An open envelope for a single role, team, member or account record.

Open on purpose. The app’s own response models on this surface grow with the product - RoleResponse gained rep_limits and manages_role_slugs, TeamResponse gained channel counts and a campaign count - and a pinned mirror would SILENTLY DROP whatever it had not caught up on. A dropped permission in a role read is the worst possible kind of stale: it reads as “they do not have it”.

object
key
additional properties
any
pagination
Any of:
object
has_next
required

Whether there are more pages

boolean
limit
required

Items per page

integer
next_cursor
Any of:
string
notes
Any of:
Array<string>
page
required

Current page number (1-indexed)

integer
total
required

Total number of items

integer
total_available
Any of:
integer
total_is_lower_bound
Any of:
boolean

422

Validation Error

object
detail
Array<object>
object
loc
required
Array
msg
required
string
type
required
string