OAuth para apps
O seu app se conecta à conta do Cord de uma empresa com um clique, sem que ninguém copie uma chave.
Quando usar
Se você está construindo um app que muitas empresas vão conectar ao Cord delas — um conector, um complemento, uma plataforma de automação —, use OAuth 2.0 em vez de pedir uma chave de API. A pessoa autoriza em uma tela do Cord, escolhe o espaço de trabalho e pode revogar o acesso quando quiser. É o mesmo mecanismo que os apps do Cord no Zapier e no Make, e o complemento do Cord para Gmail, usam.
Para uma integração da sua própria empresa, uma chave de API é mais simples.
Registrar o seu app
O registro de clientes OAuth ainda não é de autoatendimento. Escreva para contacto@cordhq.app com o nome do seu app, o domínio dele e os endereços de retorno (redirect_uri) exatos. Nós fornecemos um client_id e um client_secret. Guarde o segredo no seu servidor: o Cord só conserva a impressão digital dele e não pode mostrá-lo de novo.
Os endereços de retorno devem ser https (ou http://localhost para desenvolvimento) e são comparados de forma exata, sem curingas.
1. Solicite a autorização
Envie a pessoa para:
https://cordhq.app/oauth/authorize
?response_type=code
&client_id=cord_oc_...
&redirect_uri=https://yourapp.com/cord/callback
&scope=write
&state=RANDOM_VALUE
&code_challenge=... (opcional, PKCE)
&code_challenge_method=S256 (opcional, PKCE)
| Parâmetro | Detalhe |
|---|---|
scope |
read para somente leitura; write para leitura e escrita (inclui read). Sem scope, o Cord pede read. Você pode separar valores com espaço, vírgula ou +; qualquer outro valor responde invalid_scope. |
state |
Um valor aleatório que o seu app verifica no retorno. Impede que um terceiro injete uma autorização. |
code_challenge |
PKCE: BASE64URL(SHA256(code_verifier)), 43 caracteres sem preenchimento. Sempre recomendado; só é aceito code_challenge_method=S256. |
Se a pessoa não tiver sessão iniciada, o Cord pede que ela entre e volta para a mesma tela. Só quem tem a permissão de Configurações em um espaço de trabalho pode autorizar apps nele. A tela mostra o que o seu app poderá e não poderá fazer.
Ao autorizar, o Cord volta para o seu redirect_uri com ?code=...&state=.... Se a pessoa cancelar, volta com ?error=access_denied&state=....
2. Troque o código
O código dura 5 minutos e serve uma única vez. Faça a troca a partir do seu servidor:
curl -X POST "https://cordhq.app/api/oauth/token" \
-d grant_type=authorization_code \
-d code=cord_ac_... \
-d redirect_uri=https://yourapp.com/cord/callback \
-d client_id=cord_oc_... \
-d client_secret=cord_cs_... \
-d code_verifier=...
Você também pode enviar as credenciais do cliente com autenticação HTTP Basic.
{ "access_token": "cord_at_...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "cord_rt_...", "scope": "write" }
3. Chame a API
O access_token funciona como uma chave em toda a API v1 e no servidor MCP:
curl "https://cordhq.app/api/v1/me" -H "Authorization: Bearer cord_at_..."
4. Renove o acesso
O access token dura 1 hora. Renove-o com o refresh token, que dura 180 dias:
curl -X POST "https://cordhq.app/api/oauth/token" \
-d grant_type=refresh_token \
-d refresh_token=cord_rt_... \
-d client_id=cord_oc_... \
-d client_secret=cord_cs_...
Cada renovação entrega um refresh token novo e o anterior deixa de funcionar. Guarde sempre o novo. Se um refresh token já usado for apresentado de novo depois de 30 segundos, o Cord o trata como possível roubo e revoga a conexão inteira; a pessoa terá que autorizar de novo.
5. Revogue
curl -X POST "https://cordhq.app/api/oauth/revoke" \
-d token=cord_rt_... -d client_id=cord_oc_... -d client_secret=cord_cs_...
Responde 200 mesmo que o token não exista, para não revelar quais tokens são válidos. A pessoa também pode revogar o seu app na aba Chaves do Cord Workbench, onde ele aparece como Conexão autorizada.
PKCE
O PKCE impede que um código interceptado possa ser trocado. Gere um code_verifier aleatório de 43 a 128 caracteres (A-Z, a-z, 0-9, -, ., _, ~), guarde-o na sessão do seu servidor e envie o hash dele ao autorizar:
import { createHash, randomBytes } from 'node:crypto';
const codeVerifier = randomBytes(48).toString('base64url'); // 64 caracteres
const codeChallenge = createHash('sha256').update(codeVerifier).digest('base64url'); // 43 caracteres
// Em /oauth/authorize: code_challenge=<codeChallenge>&code_challenge_method=S256
// Em /api/oauth/token: code_verifier=<codeVerifier>
Se você enviou code_challenge ao autorizar, a troca exige o code_verifier correspondente. Se não enviou, a troca não deve levar code_verifier: um inesperado também responde invalid_grant.
Erros da autorização
Há dois tipos de erro em /oauth/authorize, e eles se comportam de forma diferente de propósito.
O Cord não volta para o seu app quando não pode confiar no endereço de retorno. A pessoa vê uma página de erro do Cord e nada é redirecionado:
| Situação | O que acontece |
|---|---|
Um client_id que não existe |
Página de erro do Cord (unknown_client). |
Um redirect_uri que não coincide exatamente com um registrado |
Página de erro do Cord. Verifique maiúsculas, barra final e porta. |
O Cord volta para o seu redirect_uri com error e o seu state quando o cliente e o endereço são válidos, mas a solicitação não:
error |
Quando |
|---|---|
unsupported_response_type |
response_type não é code. |
invalid_scope |
scope traz um valor diferente de read ou write. |
invalid_request |
code_challenge malformado, ou code_challenge_method diferente de S256. |
access_denied |
A pessoa cancelou, ou escolheu um espaço de trabalho em que não pode autorizar apps. |
https://yourapp.com/cord/callback?error=invalid_scope&state=RANDOM_VALUE
Esses redirecionamentos não levam error_description. Verifique sempre se o state é o que você enviou antes de mostrar o erro.
Erros da troca e da renovação
POST /api/oauth/token aceita o corpo como application/x-www-form-urlencoded ou como JSON, e sempre responde JSON com error e error_description:
{ "error": "invalid_grant", "error_description": "El código o el token no es válido, ya se usó o venció." }
| Código | error |
error_description |
Quando |
|---|---|---|---|
401 |
invalid_client |
Las credenciales de la aplicación no son válidas. | client_id ou client_secret incorretos, por Basic ou no corpo. |
400 |
invalid_request |
El cuerpo de la petición no se pudo leer. | O corpo não é um formulário nem um JSON válido. |
400 |
invalid_request |
Faltan code o redirect_uri. | Troca sem code ou sem redirect_uri. |
400 |
invalid_request |
Falta refresh_token. | Renovação sem refresh_token. |
400 |
invalid_request |
Este espacio ya tiene demasiadas conexiones de esta aplicación… | O espaço de trabalho já tem 25 conexões ativas do seu app. A pessoa deve revogar as que não usa. |
400 |
unsupported_grant_type |
Solo se admite authorization_code y refresh_token. | Qualquer outro grant_type. |
400 |
invalid_grant |
El código o el token no es válido, ya se usó o venció. | Código expirado ou já usado, redirect_uri diferente da usada na autorização, code_verifier que não confere, ou refresh token expirado, já usado ou de outro cliente. |
429 |
— | — | Mais de 300 requisições por minuto a partir do mesmo IP. Respeite Retry-After. |
error_description está em espanhol e pode mudar: decida pelo error.
POST /api/oauth/revoke responde 401 com error: "invalid_client" se as credenciais do cliente não forem válidas, e 200 em qualquer outro caso.
Cota
As chamadas com um token OAuth contam na cota de API do plano da empresa, assim como uma chave. A única exceção é o complemento do Cord para Gmail, que é uma superfície do próprio Cord: as chamadas dele não consomem a cota mensal, embora contem para os limites por minuto.