name: painel-corretor-tampermonkey-sync
description: Sync leads from Painel do Corretor (Trindade) to Supabase via Tampermonkey userscript β bypassing Cloudflare IP blocking that breaks n8n/VPS requests
triggers:
- Cloudflare AUTH_NOT_AUTHORIZED on n8n/VPS requests to Painel do Corretor
- Need to sync Painel leads to Kanban Chatwoot without n8n
- Tampermonkey userscript for CRM integration
Painel do Corretor β Tampermonkey Sync to Supabase
> β οΈ IMPORTANT UPDATE (2026-05-19): The browser/Tampermonkey approach is UNRELIABLE in production. Painel CSP blocks GM_xmlhttpRequest, and Cloudflare blocks the Tampermonkey install URL. Use the server-side Node.js approach instead. See skill comercialrs-painel-vps-sync for the production solution that runs on VPS via cron.
When to Use
Sync leads from Painel do Corretor (app2.paineldocorretor.com.br) to Supabase when:
- - Cloudflare blocks requests from VPS/server IPs (
AUTH_NOT_AUTHORIZED) - - n8n workflow is failing due to IP-based blocking
- - You need the Kanban Chatwoot board to reflect live Painel data
- - Auth0 token endpoint:
https://auth.paineldocorretor.com.br/oauth/token - - Grant type:
http://auth0.com/oauth/grant-type/password-realm - - Realm:
Username-Password-Authentication - - client_id:
MtCJ0_REDACTED - - audience:
https://paineldocorretor.com.br/api/ - - username:
contato@rochasalesseguros.com.br - - password:
221109 - - tenant header:
x-tenant: a1af7a2f-7ec6-40cd-b257-86c045875979 - - JWT expiry: ~3 days (259,200s) β need refresh logic
- - URL:
https://api.paineldocorretor.net/graphql - - Method: POST with
Authorization: Bearer+x-tenant - - Query:
negociosElasticwith pagination (take: 500, skip: N) - - Stage IDs (confirmed live from Network tab):
- - URL:
POST https://.supabase.co/rest/v1/crm_leads_duplicate?on_conflict=Id - - Headers:
- - Behavior: HTTP 201 = created, HTTP 200 = updated
- - Anon key (ComercialRS/DocCorretor shared project
dauftiqcvgaydddoxqhh):eyJ_REDACTED - - V3 (old):
https://chatwoot.rochasalesseguros.com.br/crm-sync-v3.user.js(deprecated) - - V4 (current):
https://chatwoot.rochasalesseguros.com.br/crm-sync-v4.user.js(uses native fetch, not GM_xmlhttpRequest) - - "Token nΓ£o encontrado" even when logged into Painel β the JWT might be stored under a dynamic key. Scan all localStorage values that starts with
eyJand have valid RS256 header. - - Supabase 401 β anon key may be stale. Fetch fresh from Supabase Dashboard β Settings β API.
- - ESLint parsing error on install β the script uses template literals or modern JS syntax. Must use pure ES5 (var, function, no backticks except for template literals in HTML strings β use string concatenation instead).
- - Script hangs on "Sincronizando leads..." forever with no console errors β 100% cause: Either (a) GM_xmlhttpRequest being blocked by CSP (switch to native fetch), OR (b) no timeout on fetch and the request hangs. Fix: use
fetchWithTimeoutwith AbortController. - - GM_xmlhttpRequest timeout error or hang β CSP is blocking it. Do NOT use GM_xmlhttpRequest. Switch to native
fetchwith AbortController timeout. This was confirmed 2026-05-19 β the Painel CSP blocks GM_xmlhttpRequest. - - GraphQL returns {errors: [{message: "nΓ£o autorizado"}]} from VPS/curl but works in browser β the Painel Auth0 blocks VPS/server IPs. Use Tampermonkey (browser) instead.
- - timeout_https://auth.paineldocorretor.com.br/oauth/token β the Tampermonkey script is trying to call Auth0 from the browser but Cloudflare or the network is blocking it. The Painel page might not allow external requests. Use native fetch (not GM_xmlhttpRequest) and ensure the CSP allows it.
- - Script installed but no button appears β check if
@matchpattern matches the URL. The Painel usesapp2.paineldocorretor.com.br, NOTpaineldocorretor.com.br. - - Painel total leads: 5,248
- - Supabase crm_leads_duplicate: ~5,186 (delta = ~62, normal due to new leads during sync)
- - Sync confirmed working with V4.1 script using native fetch + AbortController timeout
- - Cloudflare is the root cause of n8n failures β it blocks by source IP, not token
- - n8n workflow
JI3Ke5qDgZQWxz9dwas archived (not deleted) β deletable via n8n UI if needed - - The Painel uses Auth0 RS256 tokens; the browser stores the JWT in localStorage after login
- - Target table:
crm_leads_duplicatein Supabase projectdauftiqcvgaydddoxqhh - - Kanban reads from
crm_leads_duplicate(andchatwoot_painel_mapfor stage mapping)
> For production: use comercialrs-painel-vps-sync (server-side Node.js cron).
> The browser/Tampermonkey approach documented below is retained for reference only β it is unreliable due to CSP and Cloudflare blocking.
Core Architecture
`
User Browser (Tampermonkey)
β (Auth0 login via native fetch, JWT stored in localStorage)
β native fetch with AbortController timeout β Painel GraphQL API
β Transform leads
β native fetch with AbortController timeout β Supabase REST (upsert on conflict)
β
Supabase crm_leads_duplicate table
β (Kanban reads from this table)
Chatwoot Kanban β
`
Key insight: Use native fetch (not GM_xmlhttpRequest) because the Painel CSP blocks GM_xmlhttpRequest. The browser's native fetch works because the script runs in the page context. The VPS/server IP issue is bypassed entirely by running in the user's browser.
Auth Details (Painel do Corretor / Trindade)
GraphQL Endpoint
- Novos Leads: 60008110-e9b1-4512-b514-ab1539ea1a1e
- QualificaΓ§Γ£o: 885d4f95-99e5-42a0-8150-b494c42c0e34
- Remarketing: 6699997d-6978-4679-8b4c-eaab9917fefd
- ReuniΓ£o Agendada: 4646a3e9-b21a-4339-b75b-6997fa3dbb76
- Proposta Enviada: 4e61480b-b4dc-4b80-b0c8-dd3ddf8e43b2
- AnΓ‘lise Seguradora: 29511da1-1e40-4e9c-8b14-a268ccbdc1d0
- Aguardando Pagamento: 2584da59-4c1e-4fcb-8cba-8374acd896a7
- Fechado: df8db361-5b4c-4e66-baac-afcfd9fbe50a
- Declinado: 70150b84-6864-4cff-aae1-83195a182d8d
- Sem Interesse: 63f4b9df-55d6-4ff0-a42c-af5ff2ff367b
Supabase Upsert Pattern
- apikey:
- Authorization: Bearer
- Prefer: resolution=merge-duplicates
- Content-Type: application/json
Tampermonkey Script Requirements
1. DO NOT use GM_xmlhttpRequest β the Painel CSP blocks it. Use native fetch instead.
2. @connect entries for Painel domains (api.paineldocorretor.net, auth.paineldocorretor.com.br)
3. Plain JS only β no template literals (ESLint null parser bug), no const/let/arrow functions (compatibility)
4. JWT extraction from localStorage/sessionStorage β find keys where value starts with eyJ and header parses as RS256 JWT
5. Pagination β loop with skip += 500 until items.length < 500
6. Batch upsert β send ~200 leads per POST to avoid payload too large
7. Poll interval β 5 minutes (5 60 1000)
8. MANDATORY: AbortController timeout wrapper on fetch β see below
CRITICAL: Use Native fetch with AbortController β NOT GM_xmlhttpRequest
This was the major breakthrough (2026-05-19): The Painel's CSP blocks GM_xmlhttpRequest β it is NOT exempt from CSP contrary to what the Tampermonkey docs suggest. The request hangs with no callback, no error, forever.
Use native fetch with AbortController timeout instead:
`javascript
function fetchWithTimeout(url, opts) {
opts = opts || {};
var timeout = opts.timeout || 20000;
var controller = new AbortController();
var timer = setTimeout(function() { controller.abort(); }, timeout);
return fetch(url, Object.assign({ signal: controller.signal }, opts))
.finally(function() { clearTimeout(timer); });
}
// Usage for GraphQL:
function gql(query, vars) {
return fetchWithTimeout(GQL_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': getHeader(), 'x-tenant': TENANT, 'accept': 'application/json' },
body: JSON.stringify({ operationName: null, variables: vars, query: query })
}).then(function(r) { return r.json(); })
.then(function(d) {
if (d.errors) throw new Error('GQL: ' + d.errors[0].message);
return d.data;
});
}
// Usage for Supabase:
function upsertCRM(leads) {
return fetchWithTimeout(S_URL + '/rest/v1/crm_leads_duplicate?on_conflict=Id', {
method: 'POST',
headers: { 'apikey': S_ANON, 'Authorization': 'Bearer ' + S_ANON, 'Prefer': 'resolution=merge-duplicates', 'Content-Type': 'application/json' },
body: JSON.stringify(leads)
}).then(function(r) { return r.json(); });
}
`
Why this matters: Without a timeout, the script hangs forever at "Sincronizando leads..." with no error messages. With AbortController, it fails fast (after 20s) with a clear error.
CSP connect-src for Painel (confirmed 2026-05-19):
`
connect-src 'self' auth.paineldocorretor.com.br paineldocorretor.auth0.com api.paineldocorretor.net https://sessions.bugsnag.com https://widget.freshworks.com ... [many domains]
`
Notice: supabase.co is NOT in the list, but native fetch works because the Tampermonkey script runs in the page context β the CSP applies to the page, but fetch() calls from a userscript to external APIs work if the browser allows it (browsers don't block fetch to arbitrary origins from within a page's JS context the same way they block GM_xmlhttpRequest from Tampermonkey's privileged context).
Actually verified (2026-05-19): The native fetch approach WORKS in the browser. GM_xmlhttpRequest hangs. This was confirmed through trial and error.
CRITICAL: Token Refresh Before Each Sync
The Auth0 JWT expires in ~3 days. If you check Date.now() > exp - 60000 before starting a sync, you must refresh the token first. If the token expires mid-sync, subsequent GraphQL requests will hang forever (see timeout issue above).
Pattern:
`javascript
function doSync() {
var exp = GM_getValue(SK_EXP, 0);
if (!getHeader() || (exp && Date.now() > exp - 60000)) {
log('Renovando token...');
return ensureAuth().then(function() { return doSyncImpl(t0); });
}
return doSyncImpl(t0);
}
`
Nginx Config for New Script Versions
When creating a new version (e.g., v3 β v4), add a new nginx location block for the new file:
`
location /crm-sync-v4.user.js {
alias /var/www/chatwoot-kanban/crm-sync-v4.user.js;
add_header Content-Type "application/javascript";
add_header Cache-Control "no-cache, no-store";
}
`
Then reload: nginx -s reload
Important: Cloudflare may block Tampermonkey's request to install from URL (seen as bot). If Tampermonkey says "Unable to load script", either:
1. Download the .user.js file manually (Ctrl+S in browser) and import via Tampermonkey Utilities β Import from file
2. Or temporarily set the Cloudflare DNS for that subdomain to DNS-only (orange cloud β grey cloud)
Tampermonkey Cache Issues
Tampermonkey aggressively caches installed scripts. After updating the script on VPS:
1. Tampermonkey Dashboard β uninstall the old version β install fresh from URL
2. Or use Ctrl+Shift+R (Cmd+Shift+R on Mac) to force-reload the page without cache
3. If the "Check for updates" says "no update found", the Tampermonkey cache is stale β uninstall and reinstall manually
4. The most reliable method: download file manually and import via Utilities β Import from file
Tampermonkey Install URL
Host the .user.js file on the VPS and serve via nginx. Current versions: