curl --request POST \
--url https://app.sideshift.app/api/oauth/v1/team/members/permissions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"memberUserId": "<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
}
'import requests
url = "https://app.sideshift.app/api/oauth/v1/team/members/permissions"
payload = {
"memberUserId": "<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
}
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({
memberUserId: '<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
})
};
fetch('https://app.sideshift.app/api/oauth/v1/team/members/permissions', 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/permissions",
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([
'memberUserId' => '<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
]),
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/permissions"
payload := strings.NewReader("{\n \"memberUserId\": \"<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}")
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/permissions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"memberUserId\": \"<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}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.sideshift.app/api/oauth/v1/team/members/permissions")
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 \"memberUserId\": \"<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}"
response = http.request(request)
puts response.read_body{
"data": {
"success": true,
"permissions": {
"applicants": true,
"payouts": true,
"sendPayments": true,
"messages": true,
"posts": true,
"analytics": true,
"creatorDatabase": true,
"campaigns": true,
"scraperApi": true,
"signContracts": true,
"withdrawals": true,
"scopes": [
"<string>"
]
},
"emailPreferences": {
"disputes": true
},
"propagation": {}
}
}{
"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_..."
}
}Update member permissions
update a team member’s permissions and role. Requires the team:write scope.
curl --request POST \
--url https://app.sideshift.app/api/oauth/v1/team/members/permissions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"memberUserId": "<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
}
'import requests
url = "https://app.sideshift.app/api/oauth/v1/team/members/permissions"
payload = {
"memberUserId": "<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
}
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({
memberUserId: '<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
})
};
fetch('https://app.sideshift.app/api/oauth/v1/team/members/permissions', 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/permissions",
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([
'memberUserId' => '<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
]),
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/permissions"
payload := strings.NewReader("{\n \"memberUserId\": \"<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}")
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/permissions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"memberUserId\": \"<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}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.sideshift.app/api/oauth/v1/team/members/permissions")
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 \"memberUserId\": \"<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}"
response = http.request(request)
puts response.read_body{
"data": {
"success": true,
"permissions": {
"applicants": true,
"payouts": true,
"sendPayments": true,
"messages": true,
"posts": true,
"analytics": true,
"creatorDatabase": true,
"campaigns": true,
"scraperApi": true,
"signContracts": true,
"withdrawals": true,
"scopes": [
"<string>"
]
},
"emailPreferences": {
"disputes": true
},
"propagation": {}
}
}{
"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
Update a team member's role / permissions / email preferences. companyId is injected from the token tenant and must NOT be supplied. Required (validated in the use-case): memberUserId. Source: lib/api/team/update-permissions.usecase.ts + update_member_permissions call.body. The route zod (UpdatePermissionsInput) is .passthrough(). When role is Admin/Owner the platform forces full permissions regardless of the supplied permissions.
User id of the member to update.
1New role; defaults to the member's current role when omitted.
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 update across agency companies (default false).
Response
Updated permissions / email preferences and the agency-propagation summary (null when not propagated).
Update-permissions result. Source: UpdatePermissionsResult (lib/api/team/update-permissions.usecase.ts). propagation is null unless propagateToAgency was set and propagation succeeded.
Show child attributes
Show child attributes