📄 SKILL.md

← Vault

name: painel-corretor-graphql-api

description: Painel do Corretor (Trindade Tecnologia) GraphQL API — how to query leads, get JWT auth, and sync to Supabase

category: supabase


Painel do Corretor — GraphQL API

Context

The Painel do Corretor (Trindade Tecnologia) blocks REST API calls from external origins via Cloudflare. All REST endpoints (/api/crm/negocios, /api/crm/...) return 401/405 when called directly from terminals or scripts. The correct approach was found by inspecting n8n workflows on the VPS.

Key Discovery: GraphQL endpoint

`

POST https://api.paineldocorretor.net/graphql

`

Authentication uses Auth0 JWT Bearer tokens, NOT ApiKey header.

Headers

`

Authorization: Bearer

Content-Type: application/json

origin: https://app2.paineldocorretor.com.br

referer: https://app2.paineldocorretor.com.br/crm/negocios

accept: application/json

`

JWT Token

OAuth2/Auth0 JWT. Contains:

`json

{

"email": "contato@rochasalesseguros.com.br",

"iss": "https://auth.paineldocorretor.com.br/",

"sub": "auth0|6710fef....",

"aud": ["https://paineldocorretor.com.br/api/"],

"scope": "openid profile email offline_access"

}

`

The JWT expires (check exp claim). When expired, GraphQL returns:

`json

{"errors":[{"message":"The current user is not authorized to access this resource.","extensions":{"code":"AUTH_NOT_AUTHORIZED"}}]}

`

CRITICAL BUG in original workflow: Contato.Id is ALWAYS NULL

The original n8n workflow extracted contato { nome email avatar } — MISSING id field! Result: ALL 5181 leads in crm_leads_duplicate have Contato.Id = null. Without contato.id, you cannot call GetContato to fetch telefones, and you cannot match leads to Chatwoot conversations.

FIX: In FooBarQuery, ALWAYS include contato { id nome email avatar telefones }. Then for each lead where contato.id is not null, call GetContato(id) to get telefones.

Two-step workflow: FooBarQuery + GetContato

The Painel API requires TWO separate queries to get complete lead data:

Step 1 — FooBarQuery: Gets all leads with their contato.id (but NOT telefones):

`graphql

contato { id nome avatar email telefones } # id is crucial!

`

Step 2 — GetContato(id): For each unique contato.id, call:

`graphql

query GetContato($id: UUID!) {

contato(id: $id) {

id nome email documento cidade telefones avatar observacao

}

}

`

telefones is [String!]! — array of raw phone strings like ["+551199998888"]. Store as JSON string "[\"+5511...\"]" in Supabase.

In Supabase crm_leads_duplicate: