Tema
Cada llamada a la API de integración va autenticada con una credencial de tu comercio. COME identifica tu comercio a partir de esa credencial y solo te deja operar con tus descuentos y tus consumos.
Hay dos tipos de credencial. Elige una por sistema; puedes tener varias (una por punto de venta, por ejemplo).
| Cliente OAuth 2.0 (client credentials) | API key | |
|---|---|---|
| Recomendado para | ERP, e-commerce, integraciones con varios servicios | POS o scripts sencillos |
| Qué recibes al crearla | client_id (m{n}-…) y client_secret | Llave ck_{prefijo}.{secreto} |
| Qué envías a la API | Authorization: Bearer {access_token} | X-Api-Key: ck_… |
| Vigencia | Access token de 15 minutos; el secret vale hasta que lo rotes | 30, 90, 180 o 365 días (lo eliges al crearla) |
| Si se filtra | Rota el secret: el anterior deja de servir | Revoca la llave y crea otra |
| Límite por comercio | 10 clientes | 10 llaves |
Estado de la integración
Los clientes OAuth y las API keys ya se crean desde la oficina virtual de COME (COME Identity). El encabezado X-Api-Key y el permiso core.integrations son parte del contrato propuesto: se confirmarán cuando los endpoints se publiquen en COME.CORE.
Crear la credencial
- Entra a tu oficina virtual en COME Identity con un usuario de tu comercio que tenga el permiso Manage API keys and OAuth clients (
merchant.integrations.manage). Por seguridad, ese usuario debe tener la verificación en dos pasos activa. - Abre Integrations (
/portal/integrations). - Elige New OAuth client (recomendado) o New API key:
- Ponle un nombre que identifique el sistema (p. ej.
POS Sucursal Centro). - En Permissions marca el permiso de integración (
core.integrations). Si la pantalla indica que no hay permisos disponibles, pide a COME que habilite el permiso de integración para tu comercio. - En API keys, elige la vigencia en Expires in.
- Ponle un nombre que identifique el sistema (p. ej.
- Copia el secreto en ese momento: se muestra una sola vez. Guárdalo en un gestor de secretos o en variables de entorno del servidor, nunca en el código fuente.
Cliente OAuth 2.0 (client credentials)
Tu servidor intercambia client_id y client_secret por un access token de corta duración, y lo envía en cada llamada a la API.
1. Pedir el token
POST {identityUrl}/connect/token con cuerpo application/x-www-form-urlencoded:
| Campo | Valor |
|---|---|
grant_type | client_credentials |
client_id | Tu client ID |
client_secret | Tu client secret |
scope | core.integrations |
También puedes enviar client_id y client_secret en el encabezado Authorization: Basic base64(client_id:client_secret) en lugar del cuerpo.
bash
curl -s -X POST "$COME_IDENTITY_URL/connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=$COME_CLIENT_ID" \
-d "client_secret=$COME_CLIENT_SECRET" \
-d "scope=core.integrations"js
// Node 18+ (fetch nativo). Las credenciales vienen de variables de entorno del servidor.
async function requestToken() {
const response = await fetch(`${process.env.COME_IDENTITY_URL}/connect/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.COME_CLIENT_ID,
client_secret: process.env.COME_CLIENT_SECRET,
scope: 'core.integrations',
}),
})
if (!response.ok) throw new Error(`COME Identity respondió ${response.status}`)
return response.json() // { access_token, token_type: "Bearer", expires_in: 900 }
}csharp
using var http = new HttpClient();
var response = await http.PostAsync($"{identityUrl}/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = configuration["Come:ClientId"]!,
["client_secret"] = configuration["Come:ClientSecret"]!,
["scope"] = "core.integrations",
}));
response.EnsureSuccessStatusCode();
var token = await response.Content.ReadFromJsonAsync<TokenResponse>();
public record TokenResponse(
[property: JsonPropertyName("access_token")] string AccessToken,
[property: JsonPropertyName("expires_in")] int ExpiresIn);Respuesta:
json
{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 900,
"scope": "core.integrations"
}2. Llamar a la API con el token
bash
curl -s -X POST "$COME_BASE_URL/api/v1/integrations/eligibility" \
-H "Authorization: Bearer $COME_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "identificationTypeId": 1, "identification": "00000000000" }'3. Reutilizar el token
El token dura 15 minutos (expires_in: 900). No pidas uno por cada llamada: guárdalo en memoria y renuévalo cuando falte menos de un minuto para que venza, o cuando la API responda 401.
js
let cached = null
export async function getToken() {
const now = Date.now()
if (!cached || cached.expiresAt - 60_000 <= now) {
const token = await requestToken()
cached = { value: token.access_token, expiresAt: now + token.expires_in * 1000 }
}
return cached.value
}csharp
// Registra un servicio singleton y reutilízalo en todas las llamadas.
public sealed class ComeTokenProvider(HttpClient http, IConfiguration configuration)
{
private readonly SemaphoreSlim _lock = new(1, 1);
private string? _token;
private DateTimeOffset _expiresAt;
public async Task<string> GetTokenAsync()
{
if (_token is not null && _expiresAt - DateTimeOffset.UtcNow > TimeSpan.FromMinutes(1))
return _token;
await _lock.WaitAsync();
try
{
if (_token is null || _expiresAt - DateTimeOffset.UtcNow <= TimeSpan.FromMinutes(1))
{
var token = await RequestTokenAsync(); // POST /connect/token (paso 1)
_token = token.AccessToken;
_expiresAt = DateTimeOffset.UtcNow.AddSeconds(token.ExpiresIn);
}
return _token;
}
finally { _lock.Release(); }
}
}Rotar el client secret
En Integrations → Rotate secret obtienes un secret nuevo; el anterior deja de funcionar de inmediato. Actualiza la variable de entorno de tu servidor en el mismo momento para evitar cortes. Los tokens ya emitidos siguen funcionando hasta que vencen (máximo 15 minutos).
API key
Envías la llave completa en el encabezado X-Api-Key de cada llamada. No hay token intermedio.
bash
curl -s -X POST "$COME_BASE_URL/api/v1/integrations/eligibility" \
-H "X-Api-Key: $COME_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "identificationTypeId": 1, "identification": "00000000000" }'js
const response = await fetch(`${process.env.COME_BASE_URL}/api/v1/integrations/eligibility`, {
method: 'POST',
headers: {
'X-Api-Key': process.env.COME_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ identificationTypeId: 1, identification: '00000000000' }),
})csharp
using var http = new HttpClient { BaseAddress = new Uri($"{baseUrl}/api/v1/") };
http.DefaultRequestHeaders.Add("X-Api-Key", configuration["Come:ApiKey"]);
var response = await http.PostAsJsonAsync("integrations/eligibility",
new { identificationTypeId = 1, identification = "00000000000" });- Formato:
ck_+ prefijo de 16 caracteres +.+ secreto. El prefijo identifica la llave en tu oficina virtual (lo ves comock_a1b2c3d4…); el secreto solo lo conoces tú. - Vencimiento: la llave deja de funcionar en la fecha elegida al crearla. Crea la nueva antes de que venza, despliégala y revoca la anterior.
- Revocación: en Integrations → Revoke. La llave deja de funcionar de inmediato.
Errores de autenticación
| HTTP | Causa habitual | Qué hacer |
|---|---|---|
400 en /connect/token | grant_type o scope incorrectos | Revisa los campos del formulario. |
401 en /connect/token | client_id o client_secret inválidos, o cliente eliminado | Verifica las credenciales o rota el secret. |
401 en la API | Falta la credencial, el token venció o la API key fue revocada o venció | Pide un token nuevo o reemplaza la llave. |
403 en la API | La credencial no tiene el permiso core.integrations o tu comercio no está habilitado | Crea la credencial con el permiso correcto o contacta a COME. |
Buenas prácticas de seguridad
- Usa las credenciales solo desde tu servidor. Nunca en un navegador, una app móvil o un repositorio de código.
- Una credencial por sistema o sucursal: si una se compromete, solo revocas esa.
- Nunca envíes credenciales ni tokens en la URL (query string) ni los escribas en logs.
- Todas las llamadas van por HTTPS.
- Revisa periódicamente en Integrations las credenciales activas (la columna Last used ayuda) y elimina las que ya no uses. Eliminar un cliente OAuth invalida también sus tokens.

