curl --request POST \
--url https://app.sideshift.app/api/oauth/v1/ads/google/copy-settings \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"connectionId": "<string>",
"source": {
"kind": "campaign",
"campaignResource": "<string>"
},
"destination": {
"kind": "new_campaign"
}
}
'import requests
url = "https://app.sideshift.app/api/oauth/v1/ads/google/copy-settings"
payload = {
"connectionId": "<string>",
"source": {
"kind": "campaign",
"campaignResource": "<string>"
},
"destination": { "kind": "new_campaign" }
}
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({
connectionId: '<string>',
source: {kind: 'campaign', campaignResource: '<string>'},
destination: {kind: 'new_campaign'}
})
};
fetch('https://app.sideshift.app/api/oauth/v1/ads/google/copy-settings', 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/ads/google/copy-settings",
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([
'connectionId' => '<string>',
'source' => [
'kind' => 'campaign',
'campaignResource' => '<string>'
],
'destination' => [
'kind' => 'new_campaign'
]
]),
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/ads/google/copy-settings"
payload := strings.NewReader("{\n \"connectionId\": \"<string>\",\n \"source\": {\n \"kind\": \"campaign\",\n \"campaignResource\": \"<string>\"\n },\n \"destination\": {\n \"kind\": \"new_campaign\"\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/ads/google/copy-settings")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"connectionId\": \"<string>\",\n \"source\": {\n \"kind\": \"campaign\",\n \"campaignResource\": \"<string>\"\n },\n \"destination\": {\n \"kind\": \"new_campaign\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.sideshift.app/api/oauth/v1/ads/google/copy-settings")
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 \"connectionId\": \"<string>\",\n \"source\": {\n \"kind\": \"campaign\",\n \"campaignResource\": \"<string>\"\n },\n \"destination\": {\n \"kind\": \"new_campaign\"\n }\n}"
response = http.request(request)
puts response.read_body{}{
"error": {
"code": "invalid_request",
"message": "name is required",
"requestId": "req_..."
}
}{
"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_..."
}
}Draft a shallow paused Google settings copy
Read-only settings copy with exact resource lineage, complete bounded native settings and fresh source fingerprint. Only selected videos become new ads at ordinary launch. Existing destinations retain their parent budget, bidding, goals and schedule. Imported campaigns/groups are reporting-only. Unrepresentable or incomplete settings produce explicit blockers. New campaigns have fresh non-shared budgets; campaign, new group and selected ads are paused. This draft is not a created duplicate.
curl --request POST \
--url https://app.sideshift.app/api/oauth/v1/ads/google/copy-settings \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"connectionId": "<string>",
"source": {
"kind": "campaign",
"campaignResource": "<string>"
},
"destination": {
"kind": "new_campaign"
}
}
'import requests
url = "https://app.sideshift.app/api/oauth/v1/ads/google/copy-settings"
payload = {
"connectionId": "<string>",
"source": {
"kind": "campaign",
"campaignResource": "<string>"
},
"destination": { "kind": "new_campaign" }
}
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({
connectionId: '<string>',
source: {kind: 'campaign', campaignResource: '<string>'},
destination: {kind: 'new_campaign'}
})
};
fetch('https://app.sideshift.app/api/oauth/v1/ads/google/copy-settings', 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/ads/google/copy-settings",
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([
'connectionId' => '<string>',
'source' => [
'kind' => 'campaign',
'campaignResource' => '<string>'
],
'destination' => [
'kind' => 'new_campaign'
]
]),
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/ads/google/copy-settings"
payload := strings.NewReader("{\n \"connectionId\": \"<string>\",\n \"source\": {\n \"kind\": \"campaign\",\n \"campaignResource\": \"<string>\"\n },\n \"destination\": {\n \"kind\": \"new_campaign\"\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/ads/google/copy-settings")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"connectionId\": \"<string>\",\n \"source\": {\n \"kind\": \"campaign\",\n \"campaignResource\": \"<string>\"\n },\n \"destination\": {\n \"kind\": \"new_campaign\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.sideshift.app/api/oauth/v1/ads/google/copy-settings")
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 \"connectionId\": \"<string>\",\n \"source\": {\n \"kind\": \"campaign\",\n \"campaignResource\": \"<string>\"\n },\n \"destination\": {\n \"kind\": \"new_campaign\"\n }\n}"
response = http.request(request)
puts response.read_body{}{
"error": {
"code": "invalid_request",
"message": "name is required",
"requestId": "req_..."
}
}{
"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.
- Authorization URL
- https://app.sideshift.app/api/oauth/v1/authorize
- Token URL
- https://app.sideshift.app/api/oauth/v1/token
- Refresh URL
- https://app.sideshift.app/api/oauth/v1/token
Body
Read-only exact native settings source. Campaign settings require a new campaign. Ad-group settings may target a new or existing same-customer campaign. Returns a reviewable paused settings-only draft; no source ads, goal resources or campaigns are created.
Response
Read-only source candidates or settings-only draft. Listing a candidate does not establish support; the selected draft either contains full settings and provenance or returns a named blocker.
The response is of type object.