IwiConnect API docs

Your account

Users and invitations

People join a team by invitation. You can send them one at a time or a couple of hundred at once, which is the difference between onboarding a new starter and onboarding a new department.

Who you are

get /user no scope required

The identity behind the key: name, teams, role on each team, token balance, and any iwi accounts. Worth calling at startup to confirm a deployment is pointed where you think it is.

Invite one person

post /user/invite users:invite

Emails an invitation. The invitation expires if it is not accepted, and you can resend or expire it in the meantime.

FieldTypeNotes
namestringrequiredFull name, used in the email.
emailstringrequiredWork address. Must not already be on the team.
rolestringrequiredOWNER or MEMBER.
org_departmentstringoptionalDepartment value for your organisation type.
org_department_otherstringoptionalFree text when the department is other.
org_rolestringoptionalJob title.
org_role_otherstringoptionalFree text when the role is other.
Request
curl -X POST "$IWICONNECT_URL/user/invite" \
  -H "Authorization: Bearer $IWICONNECT_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Hemi Walker",
    "email": "hemi.walker@example.govt.nz",
    "role": "MEMBER",
    "org_department": "resource_consents",
    "org_role": "Consents Planner"
  }'

Invite in bulk

post /user/invite/bulk users:invite

Up to 200 invitations in one request. Each row is processed independently, so a bad address in row 40 does not stop rows 41 to 200.

Request
curl -X POST "$IWICONNECT_URL/user/invite/bulk" \
  -H "Authorization: Bearer $IWICONNECT_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "invitations": [
      { "name": "Hemi Walker", "email": "hemi.walker@example.govt.nz", "role": "MEMBER" },
      { "name": "Anahera Reti", "email": "anahera.reti@example.govt.nz", "role": "OWNER" }
    ]
  }'
Response
{
  "success": true,
  "message": "Bulk invitations processed",
  "status_code": 200,
  "response_object": {
    "invited_count": 1,
    "skipped_count": 1,
    "failed_count": 0,
    "results": [
      {
        "name": "Hemi Walker",
        "email": "hemi.walker@example.govt.nz",
        "status": "INVITED",
        "message": null,
        "invitation_id": "44ba..."
      },
      {
        "name": "Anahera Reti",
        "email": "anahera.reti@example.govt.nz",
        "status": "SKIPPED",
        "message": "Already a member of this team",
        "invitation_id": null
      }
    ]
  }
}

A row comes back as INVITED, SKIPPED or FAILED. Skipped means the person is already there or already invited, which is the normal result when you re-run a sync. Failed means something was wrong with the row, and the message says what.

Re-running a bulk invite is safe. Existing members and pending invitations are skipped rather than re-emailed, so you can drive this from a nightly HR export without spamming anyone.

Manage invitations

get /user/invitation/{id} team:read

One invitation with its status and expiry. Pending invitations are also listed on GET /team/{id}.

post /user/invitation/{id}/resend users:invite

Sends the email again and extends the expiry.

post /user/invitation/{id}/expire users:invite

Cancels a pending invitation immediately. Use this when someone leaves before they ever accepted, or when an invitation went to the wrong address.

post /user/invitation/{id}/accept no scope required

Accepts an invitation. This runs as the person being invited, during sign up, so an integration almost never calls it.

Keeping membership in sync

The pattern that works: pull GET /team/{id}, compare against your source of truth, bulk invite anyone missing, and revoke anyone who has left through DELETE /team/{id}/revoke. Run it nightly.

Revoking is immediate and there is no undo. Re-adding someone later means a fresh invitation they have to accept.