Sua primeira mudança, do início ao fim
Esta página é um único script linear, de um token de acesso recém-obtido até uma mudança que você pode ver na Prevu3D Platform, e de volta. Ela usa o fluxo Client Credentials do início ao fim, e cada ID que precisa vem de uma resposta de API anterior no mesmo script: nada para preencher manualmente.
O que você fará
Seção intitulada “O que você fará”- Obter um token de acesso: autenticar-se com Client Credentials
- Descobrir sua organização: chamar
/oauth/api-infopara obter sua URL de API e o ID da organização - Navegar até uma divisão: o primeiro filho da sua organização
- Navegar até um site: o primeiro filho dessa divisão
- Renomear o site: uma mudança pequena, evidente e reversível
- Confirmar na Prevu3D Platform: a linha de chegada
- Desfazer a renomeação: sem deixar rastros
Pré-requisitos
Seção intitulada “Pré-requisitos”- Complete primeiro o guia Fluxo Client Credentials. Seu aplicativo OAuth precisa dos scopes
read:hierarchyewrite:hierarchy, e seu usuário de serviço precisa de acesso ao conteúdo e de uma função de nível de edição em pelo menos um site (veja o modelo de segurança). - Sua organização precisa de pelo menos uma divisão contendo pelo menos um site. Este percurso navega até o primeiro que encontrar.
Etapa 1: obter um token de acesso
Seção intitulada “Etapa 1: obter um token de acesso”Endpoint: POST https://cloud-api.prevu3d.com/oauth/token
POST https://cloud-api.prevu3d.com/oauth/token HTTP/1.1Host: cloud-api.prevu3d.comAuthorization: Basic eW91ci1jbGllbnQtaWQ6eW91ci1jbGllbnQtc2VjcmV0Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials{ "hasError": false, "access_token": "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9...", "refresh_token": "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9...", "expires_in": 3599, "token_type": "bearer"}Salve o access_token. Se isso falhar, consulte o guia Fluxo Client Credentials.
Etapa 2: descobrir sua organização
Seção intitulada “Etapa 2: descobrir sua organização”Endpoint: GET https://cloud-api.prevu3d.com/oauth/api-info
Cabeçalhos: Authorization: Bearer <access_token>
{ "organization": { "id": "217ebd23-ec54-4af0-a6d6-4a441a6d1966", "name": "Test Organization" }, "scopes": ["read:hierarchy", "write:hierarchy"], "apiUrl": "https://api-ue1.prevu3d.com/realityconnect-api"}Salve apiUrl e organization.id. Cada requisição a seguir usa {apiUrl}.
Etapa 3: navegar até uma divisão
Seção intitulada “Etapa 3: navegar até uma divisão”Endpoint: GET {apiUrl}/v1/nodes/{organization_id}/browse
{ "node": { "id": "217ebd23-ec54-4af0-a6d6-4a441a6d1966", "type": "Organization", "...": "..." }, "items": [ { "id": "3d3b6f2a-9e0a-4b0a-8b0a-1c2d3e4f5a6b", "name": "North America Operations", "type": "Division", "...": "..." } ], "total": 1}Use items[0].id como division_id.
Etapa 4: navegar até um site
Seção intitulada “Etapa 4: navegar até um site”Endpoint: GET {apiUrl}/v1/nodes/{division_id}/browse
{ "node": { "id": "3d3b6f2a-9e0a-4b0a-8b0a-1c2d3e4f5a6b", "type": "Division", "...": "..." }, "items": [ { "id": "8a1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e", "name": "Chicago Plant", "type": "Site", "...": "..." } ], "total": 1}Use items[0].id como site_id, e guarde items[0].name ("Chicago Plant" neste caso); você precisará dele na etapa 7.
Etapa 5: renomear o site
Seção intitulada “Etapa 5: renomear o site”Renomear um nó é uma mudança pequena, evidente e reversível: exige write:hierarchy, não afeta mais nada, e é desfeita com uma única chamada.
Endpoint: PATCH {apiUrl}/v1/nodes/{site_id}
PATCH https://api-ue1.prevu3d.com/realityconnect-api/v1/nodes/8a1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e HTTP/1.1Authorization: Bearer <access_token>Content-Type: application/json
{ "name": "Chicago Plant (test)" }{ "id": "8a1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e", "name": "Chicago Plant (test)", "type": "Site", "...": "..." }Etapa 6: confirmar na Prevu3D Platform
Seção intitulada “Etapa 6: confirmar na Prevu3D Platform”Abra a Prevu3D Platform, navegue até a divisão da etapa 3 e abra o site da etapa 4.
Agora você deve ver o novo nome, Chicago Plant (test) neste exemplo, no topo da página do site e na lista de sites da divisão. Essa é a linha de chegada: uma mudança feita inteiramente pela API, visível no produto.
Etapa 7: desfazer a renomeação
Seção intitulada “Etapa 7: desfazer a renomeação”Endpoint: PATCH {apiUrl}/v1/nodes/{site_id}
{ "name": "Chicago Plant" }Envie o nome original do site da etapa 4. A resposta, e a Prevu3D Platform, agora mostram o site como estava antes da etapa 5.
Experimente com Python
Seção intitulada “Experimente com Python”Este script executa as etapas de 1 a 7 em ordem. Ele pausa depois da renomeação para que você possa conferir na Prevu3D Platform antes de desfazer a mudança.
import requestsimport base64
client_id = "your-client-id"client_secret = "your-client-secret"base_url = "https://cloud-api.prevu3d.com"
# Etapa 1: obter um tokentoken_response = requests.post( f"{base_url}/oauth/token", data={"grant_type": "client_credentials"}, headers={ "Authorization": f'Basic {base64.b64encode(f"{client_id}:{client_secret}".encode()).decode()}', "Content-Type": "application/x-www-form-urlencoded", },)token_response.raise_for_status()access_token = token_response.json()["access_token"]headers = {"Authorization": f"Bearer {access_token}"}
# Etapa 2: descobrir a URL da API e o ID da organizaçãoapi_info = requests.get(f"{base_url}/oauth/api-info", headers=headers).json()api_url = api_info["apiUrl"]organization_id = api_info["organization"]["id"]
# Etapa 3: navegar até a primeira divisãodivisions = requests.get(f"{api_url}/v1/nodes/{organization_id}/browse", headers=headers).json()division_id = divisions["items"][0]["id"]
# Etapa 4: navegar até o primeiro site dessa divisãosites = requests.get(f"{api_url}/v1/nodes/{division_id}/browse", headers=headers).json()site = sites["items"][0]site_id = site["id"]original_name = site["name"]
# Etapa 5: renomear o sitenew_name = f"{original_name} (test)"requests.patch(f"{api_url}/v1/nodes/{site_id}", json={"name": new_name}, headers=headers).raise_for_status()print(f"Site {site_id} renomeado para '{new_name}'.")print("Abra o site na Prevu3D Platform agora. Você deve ver o novo nome.")input("Pressione Enter assim que confirmar, para desfazer a mudança...")
# Etapa 7: desfazer a renomeaçãorequests.patch(f"{api_url}/v1/nodes/{site_id}", json={"name": original_name}, headers=headers).raise_for_status()print(f"Site {site_id} restaurado para '{original_name}'.")Versão pronta para produção
Seção intitulada “Versão pronta para produção”O passo a passo acima é um caminho feliz: um token novo, uma rede tranquila, nada para tentar de novo. Um script que continua rodando — uma importação em massa, um loop de polling, um download grande — eventualmente encontra um 429, um token de acesso que expira no meio da execução, ou uma única chamada que precisa ser repetida enquanto o resto do lote continua. Em vez de sobrecarregar cada exemplo deste site com o mesmo código defensivo, esta seção introduz um pequeno cliente reutilizável uma única vez; os exemplos que precisam dele apontam de volta para cá em vez de repeti-lo.
O RCAPIClient envolve as chamadas requests brutas acima com três coisas: ele armazena em cache o token de acesso e o renova automaticamente — de forma proativa antes de expirar, e reativa em um 401 — ele tenta novamente um 429 usando o cabeçalho Retry-After (veja Limites de taxa) em vez de adivinhar um atraso, e desiste após um número máximo de tentativas ou um tempo máximo decorrido em vez de tentar para sempre.
import timeimport base64import requests
class RCAPIClient: """Token cache + refresh, retry with backoff, and a give-up condition.
Wrap the raw `requests` calls above with this once your integration runs long enough to hit a rate limit or outlive an access token -- most bulk imports, polling loops, and long-running downloads do. """
def __init__(self, client_id, client_secret, cloud_api_base="https://cloud-api.prevu3d.com"): self.client_id = client_id self.client_secret = client_secret self.cloud_api_base = cloud_api_base self._access_token = None self._expires_at = 0 # epoch seconds
def _fetch_token(self): credentials = base64.b64encode(f"{self.client_id}:{self.client_secret}".encode()).decode() response = requests.post( f"{self.cloud_api_base}/oauth/token", data={"grant_type": "client_credentials"}, headers={"Authorization": f"Basic {credentials}"}, ) response.raise_for_status() body = response.json() self._access_token = body["access_token"] # Refresh a little before the real expiry, not exactly at it. self._expires_at = time.time() + body["expires_in"] - 60
def _token(self): if self._access_token is None or time.time() >= self._expires_at: self._fetch_token() return self._access_token
def request(self, method, url, max_attempts=6, max_elapsed=300, **kwargs): """`requests.request()` plus token refresh, 429 backoff, and a give-up condition.""" start = time.time() headers = kwargs.pop("headers", {}) for attempt in range(1, max_attempts + 1): headers["Authorization"] = f"Bearer {self._token()}" response = requests.request(method, url, headers=headers, **kwargs)
if response.status_code == 401 and attempt == 1: self._access_token = None # token was rejected outright; force one refresh continue
if response.status_code == 429: retry_after = int(response.headers.get("Retry-After", 1)) if attempt == max_attempts or time.time() - start + retry_after > max_elapsed: response.raise_for_status() time.sleep(retry_after) continue
return response return response
def poll(self, url, is_done, interval=15, max_elapsed=3600): """GET `url` through this client until `is_done(body)` or `max_elapsed` seconds pass.""" start = time.time() while True: response = self.request("GET", url) response.raise_for_status() body = response.json() if is_done(body): return body if time.time() - start >= max_elapsed: raise TimeoutError(f"Gave up polling {url} after {max_elapsed}s") time.sleep(interval)Use-o no lugar do requests diretamente, por exemplo para o token e a busca da organização das Etapas 1-2 acima:
client = RCAPIClient(client_id="your-client-id", client_secret="your-client-secret")api_info = client.request("GET", "https://cloud-api.prevu3d.com/oauth/api-info").json()O request() só recua ao ver um 429. Se uma carga de trabalho chegar perto da capacidade de um bucket sem disparar um 429 (tráfego sustentado de alto volume, por exemplo), leia X-RateLimit-Remaining e X-RateLimit-Reset de qualquer resposta e reduza a velocidade antes que o bucket se esvazie — veja Limites de taxa. O poll() constrói um loop de polling com recuo e desistência sobre o request(); Processamento e Monitoramento o utiliza.
Próximos passos
Seção intitulada “Próximos passos”Você foi de um token de acesso recém-obtido a uma mudança que pôde ver na Prevu3D Platform, e voltou.
- Como encontrar seus IDs: mais formas de resolver IDs de organização, divisão, site e outros nós além da navegação.
- Tipos de nó e hierarquia: a árvore de nós completa que você acabou de percorrer, e o que cada tipo de nó pode conter.
- Introdução: o ponto de partida organizado por tarefas para tudo o mais que a API do RealityConnect pode fazer.