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

  1. 1Creas una credencial en el portal y guardas su client_id y su client_secret en tu servidor.
  2. 2Canjeas esa credencial por un token de acceso en POST /integrations/oauth/token. El token dura 60 minutos.
  3. 3Envías el token en la cabecera Authorization: Bearer de 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.

post
/integrations/oauth/tokenhttps://api.beartrackapp.com/api/v1

Autentica 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_typestringrequerido

Siempre client_credentials.

scopestringopcional

Scopes 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'
Respuesta 200
{
  "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 401 con invalid_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 401 con invalid_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_secret nuevo. 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.
Si sospechas que un secreto se filtró, rótalo. Si un sistema dejó de usar su credencial, revócala.

Errores del canje

Cada error trae los campos error y error_description. Reacciona al código HTTP y a error.

400Bad Request
  • Petición incompleta o inválida

    invalid_request

    Falta un dato obligatorio —la cabecera Authorization, grant_type en el token o un campo del pedido como order_id— o el cuerpo no pasó la validación. error_description dice qué falló.

  • Grant no soportado

    unsupported_grant_type

    El único grant_type aceptado es client_credentials.

  • Scope no permitido

    invalid_scope

    Pediste un scope que la credencial no tiene.

401Unauthorized
  • Credenciales inválidas

    invalid_client

    El client_id no existe, el client_secret no corresponde o la credencial fue revocada. Por seguridad, la respuesta es la misma en los tres casos.

429Too Many Requests
  • Demasiadas peticiones

    too_many_requests

    Superaste el límite de tu credencial: 10 canjes de token o 120 pedidos por minuto.

503Service Unavailable
  • Servicio no disponible

    temporarily_unavailable

    Beartrack no pudo procesar la petición en este momento.