curl --request POST \
--url https://app.sideshift.app/api/oauth/v1/team/members \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"userId": "<string>",
"email": "jsmith@example.com",
"inviteId": "<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
}
}
'import requests
url = "https://app.sideshift.app/api/oauth/v1/team/members"
payload = {
"userId": "<string>",
"email": "jsmith@example.com",
"inviteId": "<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
}
}
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({
userId: '<string>',
email: 'jsmith@example.com',
inviteId: '<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
}
})
};
fetch('https://app.sideshift.app/api/oauth/v1/team/members', 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/members",
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([
'userId' => '<string>',
'email' => 'jsmith@example.com',
'inviteId' => '<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
]
]),
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/members"
payload := strings.NewReader("{\n \"userId\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"inviteId\": \"<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}")
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/members")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"userId\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"inviteId\": \"<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}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.sideshift.app/api/oauth/v1/team/members")
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 \"userId\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"inviteId\": \"<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}"
response = http.request(request)
puts response.read_body{
"data": {
"success": true
}
}{
"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_..."
}
}Add a team member
add a user to the company’s team. Requires the team:write scope.
curl --request POST \
--url https://app.sideshift.app/api/oauth/v1/team/members \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"userId": "<string>",
"email": "jsmith@example.com",
"inviteId": "<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
}
}
'import requests
url = "https://app.sideshift.app/api/oauth/v1/team/members"
payload = {
"userId": "<string>",
"email": "jsmith@example.com",
"inviteId": "<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
}
}
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({
userId: '<string>',
email: 'jsmith@example.com',
inviteId: '<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
}
})
};
fetch('https://app.sideshift.app/api/oauth/v1/team/members', 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/members",
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([
'userId' => '<string>',
'email' => 'jsmith@example.com',
'inviteId' => '<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
]
]),
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/members"
payload := strings.NewReader("{\n \"userId\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"inviteId\": \"<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}")
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/members")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"userId\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"inviteId\": \"<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}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.sideshift.app/api/oauth/v1/team/members")
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 \"userId\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"inviteId\": \"<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}"
response = http.request(request)
puts response.read_body{
"data": {
"success": true
}
}{
"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
Add a user to the company's team. companyId is injected from the token tenant and must NOT be supplied. Required (validated in the use-case): userId, email. Source: lib/api/team/add-member.usecase.ts + add_team_member call.body. The route zod (AddMemberInput) is .passthrough(). When role is Admin/Owner the platform forces full permissions.
User id of the member to add.
1Email address of the user to add.
Role to grant (defaults to Employee).
Owner, Admin, Employee Invite id when joining via self-registration (required when userId equals the caller's own id).
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
Response
Add-member success ({ success: true }).
Add-member result. Source: addTeamMember returns { success: true } (lib/api/team/add-member.usecase.ts).
Show child attributes
Show child attributes