Guía
Autenticación
La API usa OAuth 2.0 con el flujo client credentials: tu servidor cambia su client_id y su client_secret por un token de acceso y lo envía en cada petición. No hay usuarios ni redirecciones de por medio: es una integración de servidor a servidor.
Cómo funciona
- 1Creas una credencial en el portal y guardas su
client_idy suclient_secreten tu servidor. - 2Canjeas esa credencial por un token de acceso en
POST /integrations/oauth/token. El token dura 60 minutos. - 3Envías el token en la cabecera
Authorization: Bearerde cada petición, y pides uno nuevo cuando vence.
Crea tus credenciales
Entra al portal de Beartrack con un usuario de tu comercio y ve a Integraciones → API para desarrolladores. Con Crear credencial obtienes un client_id y un client_secret.
El client secret se muestra una sola vez
Cópialo en ese momento y guárdalo como variable de entorno de tu servidor. Nunca lo pongas en el navegador, en una app móvil ni en el repositorio. Si lo pierdes, rota la credencial para obtener uno nuevo.Puedes tener hasta 5 credenciales activas. Conviene una por sistema: así puedes revocar una sin cortar a las demás.
Pide un token
El token es válido por 60 minutos.
/integrations/oauth/tokenhttps://api.beartrackapp.com/api/v1Autentica la petición con HTTP Basic: el client_id como usuario y el client_secret como contraseña. También puedes enviarlos en el cuerpo como client_id y client_secret, pero nunca de las dos formas a la vez. El cuerpo va como application/x-www-form-urlencoded; también se acepta JSON.
grant_typestringrequeridoSiempre client_credentials.
scopestringopcionalScopes separados por espacio. Si lo omites, el token trae todos los de la credencial.
curl -X POST 'https://api.beartrackapp.com/api/v1/integrations/oauth/token' \
-u 'btc_tu-client-id:bts_tu-client-secret' \
-d 'grant_type=client_credentials'{
"access_token": "bta_3kq9…",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "packages:write packages:read"
}El scope packages:write permite crear pedidos. packages:read queda reservado para los próximos endpoints de consulta.
Usa el token
Envíalo en la cabecera Authorization de cada petición, con el formato Bearer <access_token>.
- Reutiliza el token mientras esté vigente (
expires_in: 3600 segundos). No pidas uno por cada pedido: el canje admite 10 peticiones por minuto por credencial. - Renuévalo unos minutos antes de que venza, o cuando una petición responda
401coninvalid_token, y reintenta esa petición una sola vez. - Guárdalo en memoria o en un almacén del servidor. Mientras dure, es tan sensible como el
client_secret.
Gestor de token
Listo para copiar en tu servidor: pide el token, lo reutiliza y lo renueva solo.
En client credentials no hay refresh token: cuando el token está por vencer, se pide otro con las mismas credenciales. Este gestor lo hace por ti:
- Reutiliza el token mientras esté vigente y pide otro un minuto antes de que venza.
- Si varias peticiones lo necesitan a la vez, hace un solo canje: no gasta el límite de 10 por minuto.
- Si una petición responde
401coninvalid_token(por ejemplo, porque rotaste la credencial), pide un token nuevo y reintenta esa petición una sola vez. - No reintenta nada más: las demás respuestas llegan tal cual a tu código, y un canje rechazado lanza una excepción con el código de error.
// beartrack.mjs · Node.js 18 o superior
const TOKEN_URL = 'https://api.beartrackapp.com/api/v1/integrations/oauth/token'
const MARGEN_MS = 60_000 // renueva un minuto antes de que venza
let token = null
let venceEn = 0
let canjeEnCurso = null
async function canjearToken() {
const credenciales = Buffer.from(
`${process.env.BEARTRACK_CLIENT_ID}:${process.env.BEARTRACK_CLIENT_SECRET}`,
).toString('base64')
const respuesta = await fetch(TOKEN_URL, {
method: 'POST',
headers: {
'Authorization': `Basic ${credenciales}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({ grant_type: 'client_credentials' }),
signal: AbortSignal.timeout(10_000),
})
const datos = await respuesta.json().catch(() => ({}))
if (!respuesta.ok) {
throw new Error(`Beartrack rechazó el canje: ${respuesta.status} ${datos.error ?? ''}`)
}
token = datos.access_token
venceEn = Date.now() + datos.expires_in * 1000
return token
}
// Token vigente. Las llamadas simultáneas comparten un solo canje.
export function obtenerToken() {
if (token && Date.now() < venceEn - MARGEN_MS) return Promise.resolve(token)
canjeEnCurso ??= canjearToken().finally(() => {
canjeEnCurso = null
})
return canjeEnCurso
}
// fetch con el token puesto. Ante 401 invalid_token (token revocado o
// credencial rotada) pide uno nuevo y reintenta una sola vez.
export async function beartrackFetch(url, opciones = {}) {
for (let intento = 1; ; intento++) {
const usado = await obtenerToken()
const respuesta = await fetch(url, {
signal: AbortSignal.timeout(30_000),
...opciones,
headers: { ...opciones.headers, 'Authorization': `Bearer ${usado}` },
})
if (respuesta.status !== 401 || intento === 2) return respuesta
const { error } = await respuesta.clone().json().catch(() => ({}))
if (error !== 'invalid_token') return respuesta
// Descarta solo el que falló: otra llamada pudo haberlo renovado ya.
if (token === usado) token = null
}
}
// Uso:
// const respuesta = await beartrackFetch('https://api.beartrackapp.com/api/v1/integrations/public-api/packages', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(pedido),
// })En PHP, el token vive en un archivo
Cada request de PHP empieza de cero, así que el gestor guarda el token en un archivo que solo tu usuario puede leer. Elige una ruta fuera del directorio público. Si tu aplicación ya tiene una caché compartida, como Redis o APCu, puedes guardarlo ahí con la misma lógica.Rota o revoca una credencial
Desde la misma pantalla del portal:
- Rotar secreto genera un
client_secretnuevo. El anterior deja de funcionar de inmediato, igual que los tokens ya emitidos: tu sistema falla hasta que cargue el secreto nuevo. - Revocar desactiva la credencial para siempre y corta sus tokens al instante. No se puede reactivar: si la necesitas de nuevo, crea otra.
Errores del canje
Cada error trae los campos error y error_description. Reacciona al código HTTP y a error.
Petición incompleta o inválida
invalid_request
Falta un dato obligatorio —la cabecera
Authorization,grant_typeen el token o un campo del pedido comoorder_id— o el cuerpo no pasó la validación.error_descriptiondice qué falló.Grant no soportado
unsupported_grant_type
El único
grant_typeaceptado esclient_credentials.Scope no permitido
invalid_scope
Pediste un
scopeque la credencial no tiene.
Credenciales inválidas
invalid_client
El
client_idno existe, elclient_secretno corresponde o la credencial fue revocada. Por seguridad, la respuesta es la misma en los tres casos.
Demasiadas peticiones
too_many_requests
Superaste el límite de tu credencial: 10 canjes de token o 120 pedidos por minuto.
Servicio no disponible
temporarily_unavailable
Beartrack no pudo procesar la petición en este momento.