Skip to content

Instantly share code, notes, and snippets.

@dvygolov
Created April 25, 2026 11:28
Show Gist options
  • Select an option

  • Save dvygolov/e26d9ee3b26ae1c10d2b6fbf0b216e1e to your computer and use it in GitHub Desktop.

Select an option

Save dvygolov/e26d9ee3b26ae1c10d2b6fbf0b216e1e to your computer and use it in GitHub Desktop.
This script helps to send a partner request for a Facebook Dataset (not usual pixel!) event if such requests are greyed in the UI
(function fbPixelPartnerRequestBootstrap() {
"use strict";
const Config = {
VERSION: "260423-ui",
GRAPHQL_PATH: "/api/graphql/",
MODAL_QUERY_DOC_ID: "24885284537836579",
VALIDATION_QUERY_DOC_ID: "9794646500614064",
MUTATION_DOC_ID: "26181988954765546",
MODAL_QUERY_NAME: "BizKitSettingsAddPartnerToAssetByBusinessIDModalQuery",
VALIDATION_QUERY_NAME: "BizKitSettingsAddPartnerToAssetByBusinessIDModalValidationQuery",
MUTATION_NAME: "BizKitSettingsAddPartnerToAssetMutation",
};
const state = {
userId: "",
fbDtsg: "",
lsd: "",
uiRoot: null,
};
function log(level, message, details) {
const prefix = `[FBPixelPartnerRequest ${Config.VERSION}]`;
if (level === "error") {
console.error(prefix, message, details ?? "");
return;
}
if (level === "warn") {
console.warn(prefix, message, details ?? "");
return;
}
console.log(prefix, message, details ?? "");
}
function escapeHtml(value) {
return String(value ?? "")
.replaceAll("&", "&")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#039;");
}
function stripFacebookPrelude(text) {
return typeof text === "string" && text.startsWith("for (;;);") ? text.slice(9) : text;
}
function getRuntimeModule(name) {
try {
if (typeof require === "function") {
return require(name);
}
} catch (_error) {
return null;
}
return null;
}
function getCurrentUserId() {
const currentUser = getRuntimeModule("CurrentUserInitialData");
return String(
currentUser?.USER_ID
|| currentUser?.ACCOUNT_ID
|| "",
);
}
function getFbDtsg() {
return String(
getRuntimeModule("DTSGInitialData")?.token
|| getRuntimeModule("DTSGInitData")?.token
|| "",
);
}
function getLsd() {
return String(getRuntimeModule("LSD")?.token || "");
}
function ensureSession() {
state.userId = state.userId || getCurrentUserId();
state.fbDtsg = state.fbDtsg || getFbDtsg();
state.lsd = state.lsd || getLsd();
if (!state.userId || !state.fbDtsg || !state.lsd) {
throw new Error("Could not extract runtime auth tokens. Run this on business.facebook.com while logged in.");
}
}
function normalizeNumericId(value, label) {
const normalized = String(value ?? "").trim().replace(/^act_/, "");
if (!/^\d+$/.test(normalized)) {
throw new Error(`Invalid ${label}: ${value}`);
}
return normalized;
}
function getUrlParam(name) {
return new URL(window.location.href).searchParams.get(name) || "";
}
function getCurrentContext() {
const businessId = normalizeNumericId(getUrlParam("business_id"), "business_id");
const assetId = normalizeNumericId(getUrlParam("selected_asset_id"), "selected_asset_id");
const assetType = String(getUrlParam("selected_asset_type") || "");
return { businessId, assetId, assetType };
}
function isPixelAssetType(assetType) {
return String(assetType || "").toLowerCase() === "pixel";
}
function getPixelAssetWarning() {
return [
"This script is intended for datasets, not plain pixels.",
"Meta returns FAILURE for pixel assets under the new-business sharing gate and does not create a pending partner request.",
"Open or create an events dataset instead, then run the tool there.",
].join(" ");
}
async function graphqlRequest(options) {
ensureSession();
const {
friendlyName,
docId,
variables,
businessId,
} = options;
const normalizedBusinessId = normalizeNumericId(businessId, "businessId");
const payload = new URLSearchParams();
payload.set("av", state.userId);
payload.set("__aaid", "0");
payload.set("__bid", normalizedBusinessId);
payload.set("__user", state.userId);
payload.set("__a", "1");
payload.set("fb_dtsg", state.fbDtsg);
payload.set("lsd", state.lsd);
payload.set("fb_api_caller_class", "RelayModern");
payload.set("fb_api_req_friendly_name", friendlyName);
payload.set("server_timestamps", "true");
payload.set("variables", JSON.stringify(variables || {}));
payload.set("doc_id", docId);
const response = await fetch(Config.GRAPHQL_PATH, {
method: "POST",
credentials: "include",
headers: {
"content-type": "application/x-www-form-urlencoded",
},
body: payload,
});
const text = stripFacebookPrelude(await response.text());
let json;
try {
json = text ? JSON.parse(text) : {};
} catch (_error) {
throw new Error(`Failed to parse GraphQL response: ${text.slice(0, 400)}`);
}
if (!response.ok || json.error || json.errors?.length) {
throw new Error(JSON.stringify(json.error || json.errors || { status: response.status, text }, null, 2));
}
return json;
}
async function loadModalConfig(context = {}) {
const resolved = {
...getCurrentContext(),
...context,
};
const businessId = normalizeNumericId(resolved.businessId, "businessId");
const assetId = normalizeNumericId(resolved.assetId, "assetId");
const json = await graphqlRequest({
friendlyName: Config.MODAL_QUERY_NAME,
docId: Config.MODAL_QUERY_DOC_ID,
businessId,
variables: {
assetID: assetId,
businessID: businessId,
},
});
const asset = json?.data?.asset;
const configs = Array.isArray(asset?.available_permission_tasks_ui_configs)
? asset.available_permission_tasks_ui_configs
: [];
return {
business: json?.data?.business || null,
asset,
configs,
context: {
businessId,
assetId,
assetType: resolved.assetType || asset?.business_asset_type || "",
},
};
}
async function validatePartner(options) {
const businessId = normalizeNumericId(options?.businessId, "businessId");
const assetId = normalizeNumericId(options?.assetId, "assetId");
const partnerBusinessId = normalizeNumericId(options?.partnerBusinessId, "partnerBusinessId");
const json = await graphqlRequest({
friendlyName: Config.VALIDATION_QUERY_NAME,
docId: Config.VALIDATION_QUERY_DOC_ID,
businessId,
variables: {
assetID: assetId,
businessID: businessId,
partnerBusinessID: partnerBusinessId,
},
});
const isValid = Boolean(json?.data?.business?.business_partnership_validation);
return {
ok: isValid,
raw: json,
};
}
function dedupe(values) {
return [...new Set((values || []).filter(Boolean).map((value) => String(value)))];
}
function resolveTaskIds(mode, configs) {
const normalizedMode = String(mode || "partial").trim().toLowerCase();
const list = Array.isArray(configs) ? configs : [];
if (normalizedMode === "partial") {
const match = list.find((item) =>
item?.task_permission_type === "PARTIAL_ACCESS_TASK"
|| item?.task_name === "ANALYZE"
|| /use events dataset/i.test(String(item?.task_label || ""))
);
if (!match?.task_id) {
throw new Error("Could not resolve partial-access task for this asset.");
}
return [String(match.task_id)];
}
if (normalizedMode === "full") {
const match = list.find((item) =>
item?.task_permission_type === "FULL_CONTROL_TASK"
|| item?.task_name === "EDIT"
|| /manage events dataset/i.test(String(item?.task_label || ""))
);
if (!match?.task_id) {
throw new Error("Could not resolve full-control task for this asset.");
}
return dedupe([
match.task_id,
...(match.hard_implied_task_ids || []),
...(match.implied_task_ids || []),
]);
}
throw new Error(`Unsupported mode: ${mode}. Use "partial" or "full".`);
}
async function addPartner(options = {}) {
const current = getCurrentContext();
const businessId = normalizeNumericId(options.businessId || current.businessId, "businessId");
const assetId = normalizeNumericId(options.assetId || current.assetId, "assetId");
const partnerBusinessId = normalizeNumericId(options.partnerBusinessId, "partnerBusinessId");
const mode = String(options.mode || "partial").toLowerCase();
const assetType = options.assetType || current.assetType;
if (isPixelAssetType(assetType)) {
throw new Error(getPixelAssetWarning());
}
const modal = await loadModalConfig({ businessId, assetId, assetType });
const taskIds = Array.isArray(options.taskIds) && options.taskIds.length
? dedupe(options.taskIds.map((value) => normalizeNumericId(value, "taskId")))
: resolveTaskIds(mode, modal.configs);
const validation = await validatePartner({
businessId,
assetId,
partnerBusinessId,
});
if (!validation.ok) {
throw new Error(`Partner validation failed for business ${partnerBusinessId}.`);
}
const json = await graphqlRequest({
friendlyName: Config.MUTATION_NAME,
docId: Config.MUTATION_DOC_ID,
businessId,
variables: {
businessID: businessId,
assetID: assetId,
surfaceParams: {
entry_point: "BIZWEB_SETTINGS_ASSETS_VIEW_DETAILS_HEADER",
flow_source: "BIZ_WEB",
tab: "EVENTS_DATASET_AND_PIXEL",
},
toBusinessID: partnerBusinessId,
taskIDs: taskIds,
shouldFetchPartnerName: false,
},
});
const result = {
businessId,
assetId,
assetType: modal.context.assetType,
assetName: modal.asset?.name || modal.asset?.business_object_name || "",
partnerBusinessId,
mode,
taskIds,
validationOk: true,
raw: json,
};
log("info", `Partner request sent for asset ${assetId} to BM ${partnerBusinessId}.`, result);
return result;
}
function extractMutationSummary(result) {
const raw = result?.raw || {};
const connection = raw?.data?.business_settings_add_partner_to_asset_connection || null;
const mutation =
raw?.data?.xfb_business_settings_add_partner_to_asset
|| raw?.data?.business_settings_add_partner_to_asset
|| raw?.data?.add_partner_to_asset
|| connection?.results
|| null;
const resultType =
mutation?.result_type
|| mutation?.resultType
|| connection?.results?.result_type
|| raw?.data?.result_type
|| "";
const policy =
mutation?.policy_result
|| mutation?.policyResult
|| mutation?.can_viewer_assign_to_agency_business_result
|| mutation?.can_viewer_share_asset_result
|| connection?.business_asset?.can_viewer_assign_to_agency_business_result
|| connection?.proxiable_object?.can_viewer_assign_to_agency_business_result
|| null;
const reasonChain = Array.isArray(policy?.reason_chain)
? policy.reason_chain.map((item) => item?.name || item).filter(Boolean)
: [];
return {
resultType,
didPassPolicy: typeof policy?.did_pass_policy === "boolean" ? policy.did_pass_policy : null,
reasonChain,
exceptionDescription:
policy?.exception?.description
|| policy?.exception?.message
|| policy?.exception_description
|| "",
};
}
function setUiStatus(type, message, details) {
const root = state.uiRoot;
if (!root) return;
const status = root.querySelector("[data-fbppr-status]");
if (!status) return;
const colors = {
info: ["#eef6ff", "#184f8b"],
success: ["#edf9f1", "#17663a"],
warn: ["#fff8e5", "#795000"],
error: ["#fff0f0", "#8a1f1f"],
};
const [background, color] = colors[type] || colors.info;
status.style.background = background;
status.style.color = color;
status.innerHTML = `
<div style="font-weight:700;margin-bottom:4px">${escapeHtml(message)}</div>
${details ? `<pre style="white-space:pre-wrap;margin:0;font:12px/1.35 ui-monospace,SFMono-Regular,Consolas,monospace">${escapeHtml(details)}</pre>` : ""}
`;
}
function setUiBusy(isBusy) {
const root = state.uiRoot;
if (!root) return;
const submit = root.querySelector("[data-fbppr-submit]");
const close = root.querySelector("[data-fbppr-close]");
if (submit) {
submit.disabled = Boolean(isBusy);
submit.textContent = isBusy ? "Sending..." : "Send partner request";
}
if (close) {
close.disabled = Boolean(isBusy);
}
}
function setSubmitBlocked(isBlocked) {
const root = state.uiRoot;
if (!root) return;
const submit = root.querySelector("[data-fbppr-submit]");
if (!submit) return;
submit.disabled = Boolean(isBlocked);
submit.style.opacity = isBlocked ? ".55" : "1";
submit.style.cursor = isBlocked ? "not-allowed" : "pointer";
}
function removeExistingUi() {
const existing = document.getElementById("fb-pixel-partner-request-ui");
if (existing) existing.remove();
state.uiRoot = null;
}
async function refreshUiContext() {
const root = state.uiRoot;
if (!root) return null;
try {
const context = getCurrentContext();
root.querySelector("[data-fbppr-asset-id]").textContent = context.assetId;
if (isPixelAssetType(context.assetType)) {
setSubmitBlocked(true);
setUiStatus("warn", "Pixel asset detected.", getPixelAssetWarning());
return null;
}
setSubmitBlocked(false);
const modal = await loadModalConfig(context);
setUiStatus("info", "Ready.", "Enter target BM ID and send the partner request.");
return modal;
} catch (error) {
setSubmitBlocked(true);
setUiStatus("error", "Could not read current dataset context.", error?.message || String(error));
return null;
}
}
function showUI() {
removeExistingUi();
const root = document.createElement("div");
root.id = "fb-pixel-partner-request-ui";
root.style.cssText = [
"position:fixed",
"right:24px",
"bottom:24px",
"z-index:2147483647",
"width:420px",
"max-width:calc(100vw - 32px)",
"background:#f7f2e8",
"color:#20262d",
"border:1px solid #d6cab7",
"border-radius:18px",
"box-shadow:0 24px 80px rgba(38,31,22,.28)",
"font:14px/1.4 Segoe UI,Arial,sans-serif",
"overflow:hidden",
].join(";");
root.innerHTML = `
<div style="padding:16px 18px;background:linear-gradient(135deg,#233429,#536d46);color:#fff">
<div style="display:flex;align-items:start;justify-content:space-between;gap:12px">
<div>
<div style="font-size:16px;font-weight:800;letter-spacing:.2px">Pixel Partner Request</div>
<div style="opacity:.82;font-size:12px;margin-top:2px">Send dataset/pixel access request to another BM</div>
<div style="opacity:.78;font-size:11px;margin-top:4px">
by <a href="https://yellowweb.top" target="_blank" rel="noopener noreferrer" style="color:#fff;text-decoration:underline">Yellow Web</a>
</div>
</div>
<button data-fbppr-close type="button" style="border:0;background:rgba(255,255,255,.14);color:#fff;border-radius:999px;width:30px;height:30px;cursor:pointer;font-size:18px;line-height:1">×</button>
</div>
</div>
<div style="padding:16px 18px">
<div style="display:grid;grid-template-columns:80px 1fr;gap:5px 10px;font-size:12px;margin-bottom:14px;color:#47515b">
<div>Asset ID</div><div data-fbppr-asset-id style="font-family:ui-monospace,SFMono-Regular,Consolas,monospace">...</div>
</div>
<label style="display:block;font-weight:700;margin-bottom:6px">Target BM ID</label>
<input data-fbppr-partner-id inputmode="numeric" placeholder="928719119582773" style="box-sizing:border-box;width:100%;border:1px solid #c8bcaa;border-radius:12px;padding:11px 12px;font:15px ui-monospace,SFMono-Regular,Consolas,monospace;background:#fffaf1;color:#1d262f;outline:none" />
<div style="display:flex;gap:10px;margin:14px 0 12px">
<label style="flex:1;border:1px solid #c8bcaa;border-radius:12px;padding:10px;background:#fffaf1;cursor:pointer">
<input type="radio" name="fbppr-mode" value="partial" checked />
<span style="font-weight:700">Partial</span>
<div style="font-size:12px;color:#65717c;margin-top:2px">Use events dataset</div>
</label>
<label style="flex:1;border:1px solid #c8bcaa;border-radius:12px;padding:10px;background:#fffaf1;cursor:pointer">
<input type="radio" name="fbppr-mode" value="full" />
<span style="font-weight:700">Full</span>
<div style="font-size:12px;color:#65717c;margin-top:2px">Manage events dataset</div>
</label>
</div>
<div style="display:flex;gap:10px;margin-top:14px">
<button data-fbppr-submit type="button" style="flex:1;border:0;border-radius:12px;padding:11px 14px;background:#2c6d55;color:#fff;font-weight:800;cursor:pointer">Send partner request</button>
</div>
<div data-fbppr-status style="margin-top:14px;border-radius:12px;padding:11px 12px;background:#eef6ff;color:#184f8b;font-size:12px">
<div style="font-weight:700">Loading context...</div>
</div>
<a data-fbppr-bookmark href="#" style="display:block;text-align:center;margin-top:10px;font-size:12px;color:#385b9f;text-decoration:underline">Copy as bookmark</a>
</div>
`;
document.body.appendChild(root);
state.uiRoot = root;
const savedPartnerId = localStorage.getItem("FBPixelPartnerRequest.partnerBusinessId") || "";
root.querySelector("[data-fbppr-partner-id]").value = savedPartnerId;
root.querySelector("[data-fbppr-close]").addEventListener("click", hideUI);
root.querySelector("[data-fbppr-bookmark]").addEventListener("click", (event) => {
event.preventDefault();
copyAsBookmarklet();
});
root.querySelector("[data-fbppr-submit]").addEventListener("click", async () => {
const partnerBusinessId = root.querySelector("[data-fbppr-partner-id]").value.trim();
const mode = root.querySelector("input[name='fbppr-mode']:checked")?.value || "partial";
if (!partnerBusinessId) {
setUiStatus("warn", "Target BM ID is required.");
return;
}
const context = getCurrentContext();
if (isPixelAssetType(context.assetType)) {
setSubmitBlocked(true);
setUiStatus("warn", "Pixel asset detected.", getPixelAssetWarning());
return;
}
try {
setUiBusy(true);
setUiStatus("info", "Sending request...", `Target BM: ${partnerBusinessId}\nMode: ${mode}`);
localStorage.setItem("FBPixelPartnerRequest.partnerBusinessId", partnerBusinessId);
const result = await addPartner({
businessId: context.businessId,
assetId: context.assetId,
assetType: context.assetType,
partnerBusinessId,
mode,
});
const summary = extractMutationSummary(result);
const details = [
`Source BM: ${result.businessId}`,
`Asset: ${result.assetName || result.assetId}`,
`Target BM: ${result.partnerBusinessId}`,
`Mode: ${result.mode}`,
`Task IDs: ${result.taskIds.join(", ")}`,
summary.resultType ? `Result: ${summary.resultType}` : "",
summary.didPassPolicy === null ? "" : `Policy passed: ${summary.didPassPolicy}`,
summary.reasonChain.length ? `Policy reasons: ${summary.reasonChain.join(" > ")}` : "",
summary.exceptionDescription ? `Policy exception: ${summary.exceptionDescription}` : "",
].filter(Boolean).join("\n");
if (summary.resultType === "FAILURE") {
setUiStatus("error", "Partner request failed.", details);
} else {
setUiStatus("success", "Partner request sent.", details);
}
} catch (error) {
setUiStatus("error", "Partner request failed.", error?.message || String(error));
} finally {
setUiBusy(false);
}
});
refreshUiContext();
return root;
}
function hideUI() {
removeExistingUi();
}
function copyTextToClipboard(text) {
if (navigator.clipboard?.writeText) {
return navigator.clipboard.writeText(text);
}
const textarea = document.createElement("textarea");
textarea.value = text;
textarea.style.position = "fixed";
textarea.style.left = "-9999px";
textarea.style.top = "-9999px";
document.body.appendChild(textarea);
textarea.select();
document.execCommand("copy");
textarea.remove();
return Promise.resolve();
}
async function copyAsBookmarklet() {
try {
const raw = `(${fbPixelPartnerRequestBootstrap.toString()})();`;
const base64 = btoa(unescape(encodeURIComponent(raw)));
const bookmarklet = `javascript:eval(decodeURIComponent(escape(atob("${base64}"))));`;
await copyTextToClipboard(bookmarklet);
setUiStatus("success", "Bookmarklet copied.", "Create a browser bookmark and paste the copied code as its URL.");
} catch (error) {
setUiStatus("error", "Failed to copy bookmarklet.", error?.message || String(error));
}
}
async function addPartnerFromPrompt() {
const context = getCurrentContext();
const partnerBusinessId = window.prompt("Partner business ID", "");
if (partnerBusinessId === null) {
throw new Error("Cancelled by user.");
}
const modeRaw = window.prompt("Access mode: partial or full", "partial");
if (modeRaw === null) {
throw new Error("Cancelled by user.");
}
const result = await addPartner({
businessId: context.businessId,
assetId: context.assetId,
assetType: context.assetType,
partnerBusinessId,
mode: modeRaw,
});
console.table({
businessId: result.businessId,
assetId: result.assetId,
partnerBusinessId: result.partnerBusinessId,
mode: result.mode,
taskIds: result.taskIds.join(", "),
});
return result;
}
function showCurrentContext() {
const context = getCurrentContext();
console.table(context);
return context;
}
function help() {
const lines = [
"Run this on business.facebook.com > Settings > Datasets & pixels with the target dataset open in the details panel.",
"The current URL must contain selected_asset_id and business_id.",
"",
"Examples:",
"FBPixelPartnerRequest.help()",
"FBPixelPartnerRequest.showCurrentContext()",
"FBPixelPartnerRequest.showUI()",
"await FBPixelPartnerRequest.loadModalConfig()",
"await FBPixelPartnerRequest.addPartnerFromPrompt()",
"await FBPixelPartnerRequest.addPartner({ partnerBusinessId: '928719119582773', mode: 'partial' })",
"await FBPixelPartnerRequest.addPartner({ businessId: '1408911316476255', assetId: '388444524197528', partnerBusinessId: '928719119582773', mode: 'full' })",
"",
"Modes:",
"- partial: uses the PARTIAL_ACCESS task from the live modal config",
"- full: uses the FULL_CONTROL task and its implied task IDs from the live modal config",
];
console.log(lines.join("\n"));
}
const api = {
version: Config.VERSION,
help,
showCurrentContext,
showUI,
hideUI,
copyAsBookmarklet,
loadModalConfig,
validatePartner,
addPartner,
addPartnerFromPrompt,
};
window.FBPixelPartnerRequest = api;
showUI();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment