Invite somebody, or give somebody more access
201 when an account was created for the address and an invitation emailed. 200 when the address already belongs to somebody in your account: the access you asked for is added, no second account is created and no email is sent — alreadyExisted and invitationEmailed in the response say which happened. 404 when any organisation named is not yours.
State the access as either organisationIds or allOrganisations: true, never both. allOrganisations grants account-wide access — every organisation the account owns, including ones added later — and only somebody who already holds it may grant it (403 whole_customer_access_denied otherwise). Every organisation named is checked against what you can reach, so nobody grants wider than they hold.
Guests (asGuest: true): somebody from another business, invited into the organisations you pick and nothing else. They never see your other organisations, your people or your settings; you see only the access you granted, and can take it back at any time. organisationIds is required and allOrganisations is refused for a guest. If they have no account, one is created for them in their own right — never a place in yours.
Sign-in follows your account’s set-up (GET /customer, signIn). Where your account links people to an identity provider and passwords are not Allowed, the new person is linked instead of emailed: invitationEmailed is false, they open Flow Payroll from the provider, and there is no password to reset. signIn: Password asks for a password of their own, for somebody outside the provider such as a bureau; 409 password_sign_in_denied where your account never issues passwords, and 409 identity_provider_not_configured for signIn: IdentityProvider where it has no provider. Giving somebody more access never changes how they sign in.
An account may hold 500 people. Only a new account of your own uses one of those places, so an invitation that turns out to be an access change works at the limit, and an account already past the limit keeps everyone it has. Three things answer 409, told apart by message.token:
user_limit_reached— the account already holds 500 people. The account owner can remove somebody who no longer needs access withDELETE /users/{principalId}, or contact support to raise the allowance.email_already_in_use— that address already signs in to a different account, so it cannot be invited here — unless you invite them as a guest.account_needs_repair— that address has an older account that support must repair before it can be given access.
curl --request POST \
--url https://api.sandbox.flowpayroll.ai/v1/administration/users \
--header 'Content-Type: application/json' \
--header 'X-Auth-Token: <api-key>' \
--data '
{
"email": "<string>",
"givenName": "<string>",
"familyName": "<string>",
"organisationIds": [
"<string>"
],
"allOrganisations": true,
"asGuest": true,
"signIn": "<string>"
}
'import requests
url = "https://api.sandbox.flowpayroll.ai/v1/administration/users"
payload = {
"email": "<string>",
"givenName": "<string>",
"familyName": "<string>",
"organisationIds": ["<string>"],
"allOrganisations": True,
"asGuest": True,
"signIn": "<string>"
}
headers = {
"X-Auth-Token": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Auth-Token': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
email: '<string>',
givenName: '<string>',
familyName: '<string>',
organisationIds: ['<string>'],
allOrganisations: true,
asGuest: true,
signIn: '<string>'
})
};
fetch('https://api.sandbox.flowpayroll.ai/v1/administration/users', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.sandbox.flowpayroll.ai/v1/administration/users",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => '<string>',
'givenName' => '<string>',
'familyName' => '<string>',
'organisationIds' => [
'<string>'
],
'allOrganisations' => true,
'asGuest' => true,
'signIn' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Auth-Token: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.sandbox.flowpayroll.ai/v1/administration/users"
payload := strings.NewReader("{\n \"email\": \"<string>\",\n \"givenName\": \"<string>\",\n \"familyName\": \"<string>\",\n \"organisationIds\": [\n \"<string>\"\n ],\n \"allOrganisations\": true,\n \"asGuest\": true,\n \"signIn\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Auth-Token", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.sandbox.flowpayroll.ai/v1/administration/users")
.header("X-Auth-Token", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"<string>\",\n \"givenName\": \"<string>\",\n \"familyName\": \"<string>\",\n \"organisationIds\": [\n \"<string>\"\n ],\n \"allOrganisations\": true,\n \"asGuest\": true,\n \"signIn\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.sandbox.flowpayroll.ai/v1/administration/users")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Auth-Token"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"<string>\",\n \"givenName\": \"<string>\",\n \"familyName\": \"<string>\",\n \"organisationIds\": [\n \"<string>\"\n ],\n \"allOrganisations\": true,\n \"asGuest\": true,\n \"signIn\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"message": {
"text": "<string>",
"token": "<string>",
"tokenArguments": [
{
"name": "<string>",
"value": null
}
]
},
"content": {
"data": {
"principalId": "<string>",
"alreadyExisted": true,
"grantedOrganisationIds": [
"<string>"
],
"alreadyHeldOrganisationIds": [
"<string>"
],
"coversWholeCustomer": true,
"invitationEmailed": true
},
"metadata": {
"dateFormat": "<string>",
"dateTimeFormat": "<string>",
"paginationToken": "<string>"
}
},
"validationIssues": [
{
"field": "<string>",
"reason": "<string>",
"reasonToken": "<string>",
"reasonTokenArguments": [
{
"name": "<string>",
"value": null
}
]
}
]
}{
"message": {
"text": "<string>",
"token": "<string>",
"tokenArguments": [
{
"name": "<string>",
"value": null
}
]
},
"content": {
"data": {
"principalId": "<string>",
"alreadyExisted": true,
"grantedOrganisationIds": [
"<string>"
],
"alreadyHeldOrganisationIds": [
"<string>"
],
"coversWholeCustomer": true,
"invitationEmailed": true
},
"metadata": {
"dateFormat": "<string>",
"dateTimeFormat": "<string>",
"paginationToken": "<string>"
}
},
"validationIssues": [
{
"field": "<string>",
"reason": "<string>",
"reasonToken": "<string>",
"reasonTokenArguments": [
{
"name": "<string>",
"value": null
}
]
}
]
}{
"message": {
"text": "<string>",
"token": "<string>",
"tokenArguments": [
{
"name": "<string>",
"value": null
}
]
},
"content": {
"data": null,
"metadata": {
"dateFormat": "<string>",
"dateTimeFormat": "<string>",
"paginationToken": "<string>"
}
},
"validationIssues": [
{
"field": "<string>",
"reason": "<string>",
"reasonToken": "<string>",
"reasonTokenArguments": [
{
"name": "<string>",
"value": null
}
]
}
]
}{
"message": {
"text": "<string>",
"token": "<string>",
"tokenArguments": [
{
"name": "<string>",
"value": null
}
]
},
"content": {
"data": null,
"metadata": {
"dateFormat": "<string>",
"dateTimeFormat": "<string>",
"paginationToken": "<string>"
}
},
"validationIssues": [
{
"field": "<string>",
"reason": "<string>",
"reasonToken": "<string>",
"reasonTokenArguments": [
{
"name": "<string>",
"value": null
}
]
}
]
}{
"message": {
"text": "<string>",
"token": "<string>",
"tokenArguments": [
{
"name": "<string>",
"value": null
}
]
},
"content": {
"data": null,
"metadata": {
"dateFormat": "<string>",
"dateTimeFormat": "<string>",
"paginationToken": "<string>"
}
},
"validationIssues": [
{
"field": "<string>",
"reason": "<string>",
"reasonToken": "<string>",
"reasonTokenArguments": [
{
"name": "<string>",
"value": null
}
]
}
]
}{
"message": {
"text": "<string>",
"token": "<string>",
"tokenArguments": [
{
"name": "<string>",
"value": null
}
]
},
"content": {
"data": null,
"metadata": {
"dateFormat": "<string>",
"dateTimeFormat": "<string>",
"paginationToken": "<string>"
}
},
"validationIssues": [
{
"field": "<string>",
"reason": "<string>",
"reasonToken": "<string>",
"reasonTokenArguments": [
{
"name": "<string>",
"value": null
}
]
}
]
}{
"message": {
"text": "<string>",
"token": "<string>",
"tokenArguments": [
{
"name": "<string>",
"value": null
}
]
},
"content": {
"data": null,
"metadata": {
"dateFormat": "<string>",
"dateTimeFormat": "<string>",
"paginationToken": "<string>"
}
},
"validationIssues": [
{
"field": "<string>",
"reason": "<string>",
"reasonToken": "<string>",
"reasonTokenArguments": [
{
"name": "<string>",
"value": null
}
]
}
]
}Authorizations
FlowPayroll JWT (raw token, no Bearer prefix)
Body
Invites somebody into your account, or gives more access to somebody already in it.
Required. The address they sign in with. It is also their identity here, so inviting the same address twice is recognised as the same person and answered as an access change.
Their first name, used in the invitation.
Their last name, used in the invitation.
The organisations to grant. State exactly one of this and allOrganisations.
Give access to every organisation the account owns, including ones added later. Only somebody who
already holds account-wide access may grant it. State exactly one of this and
organisationIds; a request giving both is refused rather than merged.
The person belongs to another business. They are invited into the organisations you pick, never into the account: they cannot see your other organisations, your people or your settings, and you can take the access back at any time. Requires OrganisationIds; AllOrganisations is refused for guests.
How they sign in. IdentityProvider links them to your account's identity provider;
Password gives them a password of their own, for somebody outside your identity provider
such as a bureau. Leave it out to use your account's default. Password is refused when your
account signs people in through its identity provider only, and IdentityProvider when it
has none.
Was this page helpful?
curl --request POST \
--url https://api.sandbox.flowpayroll.ai/v1/administration/users \
--header 'Content-Type: application/json' \
--header 'X-Auth-Token: <api-key>' \
--data '
{
"email": "<string>",
"givenName": "<string>",
"familyName": "<string>",
"organisationIds": [
"<string>"
],
"allOrganisations": true,
"asGuest": true,
"signIn": "<string>"
}
'import requests
url = "https://api.sandbox.flowpayroll.ai/v1/administration/users"
payload = {
"email": "<string>",
"givenName": "<string>",
"familyName": "<string>",
"organisationIds": ["<string>"],
"allOrganisations": True,
"asGuest": True,
"signIn": "<string>"
}
headers = {
"X-Auth-Token": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Auth-Token': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
email: '<string>',
givenName: '<string>',
familyName: '<string>',
organisationIds: ['<string>'],
allOrganisations: true,
asGuest: true,
signIn: '<string>'
})
};
fetch('https://api.sandbox.flowpayroll.ai/v1/administration/users', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.sandbox.flowpayroll.ai/v1/administration/users",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => '<string>',
'givenName' => '<string>',
'familyName' => '<string>',
'organisationIds' => [
'<string>'
],
'allOrganisations' => true,
'asGuest' => true,
'signIn' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Auth-Token: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.sandbox.flowpayroll.ai/v1/administration/users"
payload := strings.NewReader("{\n \"email\": \"<string>\",\n \"givenName\": \"<string>\",\n \"familyName\": \"<string>\",\n \"organisationIds\": [\n \"<string>\"\n ],\n \"allOrganisations\": true,\n \"asGuest\": true,\n \"signIn\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Auth-Token", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.sandbox.flowpayroll.ai/v1/administration/users")
.header("X-Auth-Token", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"<string>\",\n \"givenName\": \"<string>\",\n \"familyName\": \"<string>\",\n \"organisationIds\": [\n \"<string>\"\n ],\n \"allOrganisations\": true,\n \"asGuest\": true,\n \"signIn\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.sandbox.flowpayroll.ai/v1/administration/users")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Auth-Token"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"<string>\",\n \"givenName\": \"<string>\",\n \"familyName\": \"<string>\",\n \"organisationIds\": [\n \"<string>\"\n ],\n \"allOrganisations\": true,\n \"asGuest\": true,\n \"signIn\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"message": {
"text": "<string>",
"token": "<string>",
"tokenArguments": [
{
"name": "<string>",
"value": null
}
]
},
"content": {
"data": {
"principalId": "<string>",
"alreadyExisted": true,
"grantedOrganisationIds": [
"<string>"
],
"alreadyHeldOrganisationIds": [
"<string>"
],
"coversWholeCustomer": true,
"invitationEmailed": true
},
"metadata": {
"dateFormat": "<string>",
"dateTimeFormat": "<string>",
"paginationToken": "<string>"
}
},
"validationIssues": [
{
"field": "<string>",
"reason": "<string>",
"reasonToken": "<string>",
"reasonTokenArguments": [
{
"name": "<string>",
"value": null
}
]
}
]
}{
"message": {
"text": "<string>",
"token": "<string>",
"tokenArguments": [
{
"name": "<string>",
"value": null
}
]
},
"content": {
"data": {
"principalId": "<string>",
"alreadyExisted": true,
"grantedOrganisationIds": [
"<string>"
],
"alreadyHeldOrganisationIds": [
"<string>"
],
"coversWholeCustomer": true,
"invitationEmailed": true
},
"metadata": {
"dateFormat": "<string>",
"dateTimeFormat": "<string>",
"paginationToken": "<string>"
}
},
"validationIssues": [
{
"field": "<string>",
"reason": "<string>",
"reasonToken": "<string>",
"reasonTokenArguments": [
{
"name": "<string>",
"value": null
}
]
}
]
}{
"message": {
"text": "<string>",
"token": "<string>",
"tokenArguments": [
{
"name": "<string>",
"value": null
}
]
},
"content": {
"data": null,
"metadata": {
"dateFormat": "<string>",
"dateTimeFormat": "<string>",
"paginationToken": "<string>"
}
},
"validationIssues": [
{
"field": "<string>",
"reason": "<string>",
"reasonToken": "<string>",
"reasonTokenArguments": [
{
"name": "<string>",
"value": null
}
]
}
]
}{
"message": {
"text": "<string>",
"token": "<string>",
"tokenArguments": [
{
"name": "<string>",
"value": null
}
]
},
"content": {
"data": null,
"metadata": {
"dateFormat": "<string>",
"dateTimeFormat": "<string>",
"paginationToken": "<string>"
}
},
"validationIssues": [
{
"field": "<string>",
"reason": "<string>",
"reasonToken": "<string>",
"reasonTokenArguments": [
{
"name": "<string>",
"value": null
}
]
}
]
}{
"message": {
"text": "<string>",
"token": "<string>",
"tokenArguments": [
{
"name": "<string>",
"value": null
}
]
},
"content": {
"data": null,
"metadata": {
"dateFormat": "<string>",
"dateTimeFormat": "<string>",
"paginationToken": "<string>"
}
},
"validationIssues": [
{
"field": "<string>",
"reason": "<string>",
"reasonToken": "<string>",
"reasonTokenArguments": [
{
"name": "<string>",
"value": null
}
]
}
]
}{
"message": {
"text": "<string>",
"token": "<string>",
"tokenArguments": [
{
"name": "<string>",
"value": null
}
]
},
"content": {
"data": null,
"metadata": {
"dateFormat": "<string>",
"dateTimeFormat": "<string>",
"paginationToken": "<string>"
}
},
"validationIssues": [
{
"field": "<string>",
"reason": "<string>",
"reasonToken": "<string>",
"reasonTokenArguments": [
{
"name": "<string>",
"value": null
}
]
}
]
}{
"message": {
"text": "<string>",
"token": "<string>",
"tokenArguments": [
{
"name": "<string>",
"value": null
}
]
},
"content": {
"data": null,
"metadata": {
"dateFormat": "<string>",
"dateTimeFormat": "<string>",
"paginationToken": "<string>"
}
},
"validationIssues": [
{
"field": "<string>",
"reason": "<string>",
"reasonToken": "<string>",
"reasonTokenArguments": [
{
"name": "<string>",
"value": null
}
]
}
]
}