curl --request POST \
--url https://app.sideshift.app/api/oauth/v1/team/invites \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"email": "jsmith@example.com",
"code": "<string>",
"permissions": {
"scopes": [
"<string>"
],
"applicants": true,
"payouts": true,
"sendPayments": true,
"messages": true,
"posts": true,
"analytics": true,
"creatorDatabase": true,
"campaigns": true,
"scraperApi": true,
"signContracts": true,
"withdrawals": true
},
"propagateToAgency": true,
"subaccountOnly": true,
"inviterId": "<string>"
}
'import requests
url = "https://app.sideshift.app/api/oauth/v1/team/invites"
payload = {
"email": "jsmith@example.com",
"code": "<string>",
"permissions": {
"scopes": ["<string>"],
"applicants": True,
"payouts": True,
"sendPayments": True,
"messages": True,
"posts": True,
"analytics": True,
"creatorDatabase": True,
"campaigns": True,
"scraperApi": True,
"signContracts": True,
"withdrawals": True
},
"propagateToAgency": True,
"subaccountOnly": True,
"inviterId": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
email: 'jsmith@example.com',
code: '<string>',
permissions: {
scopes: ['<string>'],
applicants: true,
payouts: true,
sendPayments: true,
messages: true,
posts: true,
analytics: true,
creatorDatabase: true,
campaigns: true,
scraperApi: true,
signContracts: true,
withdrawals: true
},
propagateToAgency: true,
subaccountOnly: true,
inviterId: '<string>'
})
};
fetch('https://app.sideshift.app/api/oauth/v1/team/invites', 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://app.sideshift.app/api/oauth/v1/team/invites",
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' => 'jsmith@example.com',
'code' => '<string>',
'permissions' => [
'scopes' => [
'<string>'
],
'applicants' => true,
'payouts' => true,
'sendPayments' => true,
'messages' => true,
'posts' => true,
'analytics' => true,
'creatorDatabase' => true,
'campaigns' => true,
'scraperApi' => true,
'signContracts' => true,
'withdrawals' => true
],
'propagateToAgency' => true,
'subaccountOnly' => true,
'inviterId' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://app.sideshift.app/api/oauth/v1/team/invites"
payload := strings.NewReader("{\n \"email\": \"jsmith@example.com\",\n \"code\": \"<string>\",\n \"permissions\": {\n \"scopes\": [\n \"<string>\"\n ],\n \"applicants\": true,\n \"payouts\": true,\n \"sendPayments\": true,\n \"messages\": true,\n \"posts\": true,\n \"analytics\": true,\n \"creatorDatabase\": true,\n \"campaigns\": true,\n \"scraperApi\": true,\n \"signContracts\": true,\n \"withdrawals\": true\n },\n \"propagateToAgency\": true,\n \"subaccountOnly\": true,\n \"inviterId\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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://app.sideshift.app/api/oauth/v1/team/invites")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"jsmith@example.com\",\n \"code\": \"<string>\",\n \"permissions\": {\n \"scopes\": [\n \"<string>\"\n ],\n \"applicants\": true,\n \"payouts\": true,\n \"sendPayments\": true,\n \"messages\": true,\n \"posts\": true,\n \"analytics\": true,\n \"creatorDatabase\": true,\n \"campaigns\": true,\n \"scraperApi\": true,\n \"signContracts\": true,\n \"withdrawals\": true\n },\n \"propagateToAgency\": true,\n \"subaccountOnly\": true,\n \"inviterId\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.sideshift.app/api/oauth/v1/team/invites")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"jsmith@example.com\",\n \"code\": \"<string>\",\n \"permissions\": {\n \"scopes\": [\n \"<string>\"\n ],\n \"applicants\": true,\n \"payouts\": true,\n \"sendPayments\": true,\n \"messages\": true,\n \"posts\": true,\n \"analytics\": true,\n \"creatorDatabase\": true,\n \"campaigns\": true,\n \"scraperApi\": true,\n \"signContracts\": true,\n \"withdrawals\": true\n },\n \"propagateToAgency\": true,\n \"subaccountOnly\": true,\n \"inviterId\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"success": true,
"message": "<string>",
"inviteId": "<string>",
"invite": {}
}
}{
"error": {
"code": "unauthorized",
"message": "Missing bearer access token",
"requestId": "req_..."
}
}{
"error": {
"code": "insufficient_scope",
"message": "Requires scope 'campaigns:write'",
"requestId": "req_..."
}
}{
"error": {
"code": "not_found",
"message": "Resource not found",
"requestId": "req_..."
}
}{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded",
"requestId": "req_..."
}
}Send a team invite
Create and send a team-member invitation for the company. Requires the team:write scope.
curl --request POST \
--url https://app.sideshift.app/api/oauth/v1/team/invites \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"email": "jsmith@example.com",
"code": "<string>",
"permissions": {
"scopes": [
"<string>"
],
"applicants": true,
"payouts": true,
"sendPayments": true,
"messages": true,
"posts": true,
"analytics": true,
"creatorDatabase": true,
"campaigns": true,
"scraperApi": true,
"signContracts": true,
"withdrawals": true
},
"propagateToAgency": true,
"subaccountOnly": true,
"inviterId": "<string>"
}
'import requests
url = "https://app.sideshift.app/api/oauth/v1/team/invites"
payload = {
"email": "jsmith@example.com",
"code": "<string>",
"permissions": {
"scopes": ["<string>"],
"applicants": True,
"payouts": True,
"sendPayments": True,
"messages": True,
"posts": True,
"analytics": True,
"creatorDatabase": True,
"campaigns": True,
"scraperApi": True,
"signContracts": True,
"withdrawals": True
},
"propagateToAgency": True,
"subaccountOnly": True,
"inviterId": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
email: 'jsmith@example.com',
code: '<string>',
permissions: {
scopes: ['<string>'],
applicants: true,
payouts: true,
sendPayments: true,
messages: true,
posts: true,
analytics: true,
creatorDatabase: true,
campaigns: true,
scraperApi: true,
signContracts: true,
withdrawals: true
},
propagateToAgency: true,
subaccountOnly: true,
inviterId: '<string>'
})
};
fetch('https://app.sideshift.app/api/oauth/v1/team/invites', 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://app.sideshift.app/api/oauth/v1/team/invites",
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' => 'jsmith@example.com',
'code' => '<string>',
'permissions' => [
'scopes' => [
'<string>'
],
'applicants' => true,
'payouts' => true,
'sendPayments' => true,
'messages' => true,
'posts' => true,
'analytics' => true,
'creatorDatabase' => true,
'campaigns' => true,
'scraperApi' => true,
'signContracts' => true,
'withdrawals' => true
],
'propagateToAgency' => true,
'subaccountOnly' => true,
'inviterId' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://app.sideshift.app/api/oauth/v1/team/invites"
payload := strings.NewReader("{\n \"email\": \"jsmith@example.com\",\n \"code\": \"<string>\",\n \"permissions\": {\n \"scopes\": [\n \"<string>\"\n ],\n \"applicants\": true,\n \"payouts\": true,\n \"sendPayments\": true,\n \"messages\": true,\n \"posts\": true,\n \"analytics\": true,\n \"creatorDatabase\": true,\n \"campaigns\": true,\n \"scraperApi\": true,\n \"signContracts\": true,\n \"withdrawals\": true\n },\n \"propagateToAgency\": true,\n \"subaccountOnly\": true,\n \"inviterId\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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://app.sideshift.app/api/oauth/v1/team/invites")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"jsmith@example.com\",\n \"code\": \"<string>\",\n \"permissions\": {\n \"scopes\": [\n \"<string>\"\n ],\n \"applicants\": true,\n \"payouts\": true,\n \"sendPayments\": true,\n \"messages\": true,\n \"posts\": true,\n \"analytics\": true,\n \"creatorDatabase\": true,\n \"campaigns\": true,\n \"scraperApi\": true,\n \"signContracts\": true,\n \"withdrawals\": true\n },\n \"propagateToAgency\": true,\n \"subaccountOnly\": true,\n \"inviterId\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.sideshift.app/api/oauth/v1/team/invites")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"jsmith@example.com\",\n \"code\": \"<string>\",\n \"permissions\": {\n \"scopes\": [\n \"<string>\"\n ],\n \"applicants\": true,\n \"payouts\": true,\n \"sendPayments\": true,\n \"messages\": true,\n \"posts\": true,\n \"analytics\": true,\n \"creatorDatabase\": true,\n \"campaigns\": true,\n \"scraperApi\": true,\n \"signContracts\": true,\n \"withdrawals\": true\n },\n \"propagateToAgency\": true,\n \"subaccountOnly\": true,\n \"inviterId\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"success": true,
"message": "<string>",
"inviteId": "<string>",
"invite": {}
}
}{
"error": {
"code": "unauthorized",
"message": "Missing bearer access token",
"requestId": "req_..."
}
}{
"error": {
"code": "insufficient_scope",
"message": "Requires scope 'campaigns:write'",
"requestId": "req_..."
}
}{
"error": {
"code": "not_found",
"message": "Resource not found",
"requestId": "req_..."
}
}{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded",
"requestId": "req_..."
}
}Authorizations
OAuth 2.1 authorization code + PKCE (S256). Tokens are tenant-bound (company_id) and scoped. Discover endpoints via /.well-known/oauth-authorization-server.
Body
Create a team invitation. companyId is injected from the token tenant and must NOT be supplied. Required (validated in the use-case): email, code. Source: lib/api/team/create-invite.usecase.ts + lib/mcp/tools/team.ts create_team_invite call.body. The route zod (CreateInviteInput) is .passthrough().
Email address to invite.
Unique invite code used for tracking / the accept link.
1Role to grant (defaults to Employee). Granting Admin forces full permissions.
Owner, Admin, Employee Team permissions share the public capability scope vocabulary. When scopes is present it is the exact grant, including an empty array for no access, and the boolean fields are compatibility projections. Owners always retain full access. Admin and Employee grants can be customized. Omitting scopes retains the legacy permission behavior. A caller cannot grant more access than they hold.
Show child attributes
Show child attributes
Per-member email-notification preferences. Source: lib/team-email-preferences.ts TEAM_EMAIL_PREFERENCE_KEYS + normalizeTeamEmailPreferences (always returns the closed key set).
Show child attributes
Show child attributes
Propagate the grant to all agency companies (default false; may be downgraded to false for subaccount-only inviters).
Restrict access to this subaccount only (default false).
User id recorded as the inviter (defaults to the caller's user id).
Response
Result of creating a team invite: a new pending invite (inviteId + invite), or a message when an existing user was auto-added / already a member.
Create-invite result. Source: createTeamInvite use-case (lib/api/team/create-invite.usecase.ts). Three success shapes share { success:true }: (a) already-a-member { message }, (b) existing-user-auto-added { message }, (c) new pending invite { inviteId, invite }.
Show child attributes
Show child attributes