Autenticación
Autenticación OAuth2 Client Credentials con Basic Auth. Obtienes un access_token de corta duración que viaja en cada llamada.
La autenticación en la API de Zertiban se basa en el estándar OAuth2 Client Credentials, utilizando Basic Auth para la obtención del token. Este mecanismo está diseñado para integraciones server-to-server seguras, en las que el cliente intercambia sus credenciales por un access_token de corta duración que se utiliza en todas las llamadas posteriores a la API.
Las credenciales (clientId y clientSecret) no se envían en el cuerpo de la petición, sino en el header mediante Basic Auth. Esto significa que deben incluirse en el encabezado Authorization, codificadas automáticamente por los clientes HTTP estándar.
Endpoint
POST/idp/oauth2/token con grant_type=client_credentials.
Las credenciales van en el header
Authorization como Basic Auth, no en el cuerpo de la petición.
La opción -u clientId:clientSecret de curl, auth=(id, secret) de Python requests y auth: { username, password } de axios construyen este header automáticamente.
Nota histórica: scope no es necesario
En documentación previa de PagaFactu se añadía también --data-urlencode 'scope=openid profile api'. No es necesario.
Pide el token
curl -X POST https://nc-api-sandbox.zertiban.com/idp/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-u '{clientId}:{clientSecret}' \
--data-urlencode 'grant_type=client_credentials'import requests
response = requests.post(
"https://nc-api-sandbox.zertiban.com/idp/oauth2/token",
auth=(CLIENT_ID, CLIENT_SECRET),
data={"grant_type": "client_credentials"}
)
token = response.json()["access_token"]
expires_in = response.json()["expires_in"]const credentials = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64');
const response = await fetch('https://nc-api-sandbox.zertiban.com/idp/oauth2/token', {
method: 'POST',
headers: {
Authorization: `Basic ${credentials}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'grant_type=client_credentials'
});
const { access_token, expires_in } = await response.json();const response = await axios.post(
'https://nc-api-sandbox.zertiban.com/idp/oauth2/token',
'grant_type=client_credentials',
{
auth: { username: CLIENT_ID, password: CLIENT_SECRET },
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}
);
const { access_token, expires_in } = response.data;TokenResponse token = WebClient.create("https://nc-api-sandbox.zertiban.com")
.post().uri("/idp/oauth2/token")
.headers(h -> h.setBasicAuth(clientId, clientSecret))
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData("grant_type", "client_credentials"))
.retrieve()
.bodyToMono(TokenResponse.class)
.block();
String accessToken = token.getAccessToken();
int expiresIn = token.getExpiresIn(); Recibe el access_token
{
"access_token": "eyJraWQiOiJ...",
"token_type": "Bearer",
"expires_in": 300
}Renueva antes de que caduque
El token dura 300 segundos (5 minutos) por defecto. Usa siempre el valor de expires_in de la respuesta, no lo pongas fijo en tu código, ya que puede variar. Renuévalo antes de que caduque para evitar errores 401.
Usa el token en cada llamada
A partir de aquí, todas las llamadas llevan estos dos headers:
Authorization: Bearer {access_token}
x-tenant-id: {businessUuid}| Header | Descripción |
|---|---|
Authorization | Token devuelto en el paso 2, con prefijo Bearer |
x-tenant-id | Tu businessUuid, identifica el negocio sobre el que operas |