Documentación para developers
Todo lo que necesitás para construir una App de Stockcito: manifiesto, permisos, Public API, extensiones de UI, automatizaciones y Marketplace — explicado de punta a punta, con ejemplos reales.
Introducción
Qué es una App de Stockcito
Una App es un servicio externo (tu propio servidor, en el lenguaje que quieras) que se conecta a la cuenta de un comercio de Stockcito para sumarle una función: leer sus pedidos y productos por una API, mostrar un panel propio dentro de su Dashboard, reaccionar automáticamente cuando pasa algo (Stockcito Flow), o agregar un módulo a su tienda online pública (DNA Modules).
Tu app nunca corre código dentro de Stockcito. Todo pasa por HTTP: Stockcito te llama a vos (webhooks, UI extensions, Flow actions) o vos llamás a Stockcito (Public API) usando un token que te entrega el comercio al instalarte. Todo request entre las dos partes va firmado con HMAC para que nadie pueda hacerse pasar por Stockcito ni por tu app.
Empezar
Tu cuenta de developer
Es una cuenta separada de la cuenta de un comercio — nunca compartís login con la tienda que te instala.
Creá tu cuenta de developer
Registrate en /developers con tu email — no es lo mismo que crear una cuenta de comercio.
Registrá tu primera App
Nombre, descripción, categoría y la URL base de tu servidor (endpoints.baseUrl).
Escribí tu manifiesto
El JSON que declara qué puede hacer tu app (ver la sección El manifiesto más abajo).
Probala en una cuenta de prueba
Instalala en un comercio de prueba propio antes de mandarla a revisión para el Marketplace.
Desde tu panel de developer ves todas tus apps, y para cada instalación un resumen de entregas (webhooks enviados, si fallaron o no) — pero nunca el contenido de negocio del comercio (no ves sus ventas ni sus clientes reales, solo que "la entrega #123 a la instalación #45 falló con HTTP 500").
Referencia
El manifiesto
Un único JSON que describe todo lo que tu app declara: quién es, qué permisos pide, y qué extensiones ofrece.
{
"key": "acme.puntos",
"name": "Acme Puntos",
"description": "Programa de puntos por compra",
"category": "Crecimiento",
"version": "1.0.0",
"author": { "name": "Acme SRL", "url": "https://acme.dev" },
"roles": ["owner", "admin"],
"scopes": ["orders:read", "customers:read"],
"events": ["order.paid"],
"extensions": {
"admin": ["dashboard.card"],
"dna": [{ "key": "puntos-widget", "name": "Widget de puntos" }],
"automationActions": [{ "key": "sumar_puntos", "name": "Sumar puntos" }]
},
"endpoints": {
"baseUrl": "https://acme.dev",
"webhookPath": "/webhook"
}
}keystringNamespace único con un punto, ej. acme.puntos. Es tu identificador estable — no lo cambies entre versiones.
categoryenumUna de: Escritorio, Comercio, Contenido, Crecimiento, Integraciones, Sistema.
rolesstring[]Qué roles del comercio pueden instalar tu app: owner, admin, manager, cajero. Por defecto solo el dueño.
scopesstring[]Los permisos que vas a usar en la Public API (ver la sección Scopes). Se muestran tal cual en la pantalla de consentimiento que ve el comercio antes de instalarte.
eventsstring[]Eventos de webhook a los que te suscribís (order.created, order.paid, order.shipped, order.cancelled, product.published, inventory.low, customer.registered).
extensions.adminstring[]Qué extensiones de UI vas a servir dentro del Dashboard: dashboard.card, order.sidebar, product.sidebar, customer.sidebar.
extensions.dnaobject[]Módulos que tu app ofrece para insertar en la tienda pública del comercio (Studio → Diseño del inicio).
extensions.automationActionsobject[]Acciones que Stockcito Flow puede invocar en tu servidor cuando el comercio arma una automatización.
endpoints.baseUrlurlLa raíz de tu servidor. Todo el resto de las rutas (webhook, UI, actions) se arman relativas a esta URL. Tiene que ser HTTPS pública — Stockcito rechaza IPs privadas o localhost por seguridad (anti-SSRF).
endpoints.webhookPathpathDónde recibís los eventos declarados en events. Default: /webhook.
Permisos
Scopes disponibles
orders:readVer pedidosorders:writeCrear o modificar pedidosproducts:readVer productosproducts:writeCrear o modificar productoscustomers:readVer clientescustomers:writeCrear o modificar clientesinventory:readVer inventarioinventory:writeModificar inventariosales:readVer ventassales:writeCrear o modificar ventasshipping:readVer zonas de envíoshipping:writeModificar zonas de envíoHoy los scopes de *:read son los que habilitan cada recurso de la Public API (ver más abajo). Los de *:write quedan declarados para uso futuro — pedilos solo si tu app realmente los necesita, ya que el comercio los ve todos en la pantalla de consentimiento antes de instalarte.
Flujo
Instalación y consentimiento
El comercio nunca instala tu app a ciegas — siempre ve una pantalla de consentimiento primero.
El dueño de la tienda pega la URL de tu manifiesto (o lo pega directo en JSON) en Configuración → Complementos. Stockcito lo descarga, lo valida, y le muestra tu nombre, descripción, versión y la lista de permisos en español simple — "esta app va a poder: ver pedidos, ver clientes" — antes de que confirme nada.
Al confirmar, Stockcito genera un token de acceso único para esa instalación y lo muestra una sola vez en pantalla. Ese es el token que el comercio (o vos, si tu app lo pide) usa como Authorization: Bearer <token> contra la Public API. Se guarda hasheado del lado de Stockcito — si se pierde, no hay forma de recuperarlo, solo reinstalar.
Referencia
Public API
Lecturas de solo lectura sobre los datos del comercio que te instaló, autenticadas por token.
Base URLurlhttps://api.stockcito.com/api/public/v1
AuthorizationheaderBearer <token de la instalación> — el que se generó al instalarte.
Stockcito-VersionheaderObligatorio en cada request, valor actual: 2026-09. Versionado por fecha para poder introducir cambios sin romper integraciones existentes.
Rate limitlímite60 requests por minuto, por token (no por IP) — separado del límite general de la API.
Recursos disponibles (todos de solo lectura, paginados con page/perPage):
GET /productsproducts:readGET /customerscustomers:readGET /salessales:readGET /inventoryinventory:readGET /ordersorders:readGET /returnsorders:readGET /shippingshipping:readcurl "https://api.stockcito.com/api/public/v1/products?page=1&perPage=20" \
-H "Authorization: Bearer TU_TOKEN" \
-H "Stockcito-Version: 2026-09"
# {"data":[...], "total":123, "page":1, "perPage":20}Referencia
Extensiones de UI
Tu app puede mostrar contenido propio dentro del Dashboard del comercio, sin escribir HTML/CSS/JS.
Si declarás dashboard.card en extensions.admin, Stockcito le hace un GET firmado a {baseUrl}/stockcito-ui/dashboard-card y renderiza lo que respondas como una tarjeta más en el Dashboard del comercio. Nunca mandás HTML: mandás un árbol de nodos declarativos, con enums cerrados, para que nunca puedas inyectar nada fuera de lo previsto.
// Tu servidor responde esto a GET /stockcito-ui/dashboard-card
{
"type": "card",
"title": "Puntos acumulados",
"children": [
{ "type": "text", "value": "1.240 puntos activos", "weight": "bold" },
{ "type": "badge", "label": "+80 esta semana", "tone": "success" },
{ "type": "link", "label": "Ver detalle", "href": "https://acme.dev/panel" }
]
}Nodos disponibles: text, badge, link, stack (agrupa en fila o columna) y card (anida los anteriores). Máximo 20 hijos por nivel y 20KB de respuesta — si tu servidor está caído, tarda demasiado, o responde algo inválido, Stockcito simplemente no muestra la tarjeta, nunca rompe el Dashboard del comercio.
Cada request firmado incluye X-Stockcito-Timestamp y X-Stockcito-Signature: sha256=..., calculada como HMAC-SHA256(tu_clave_de_firma, timestamp + "." + path). Verificala del lado del servidor y rechazá cualquier timestamp con más de 5 minutos de diferencia, para evitar ataques de repetición.
Verificar la firma en Node/Express:
const crypto = require("crypto")
const SIGNING_KEY = process.env.STOCKCITO_SIGNING_KEY // te lo dio Stockcito al instalarte
const TOLERANCE_SECONDS = 300
app.get("/stockcito-ui/dashboard-card", (req, res) => {
const timestamp = req.header("X-Stockcito-Timestamp")
const signatureHeader = req.header("X-Stockcito-Signature") || ""
const path = req.path // "/stockcito-ui/dashboard-card"
const now = Math.floor(Date.now() / 1000)
if (!timestamp || Math.abs(now - Number(timestamp)) > TOLERANCE_SECONDS) {
return res.status(401).send("Timestamp inválido o vencido")
}
const expected = crypto
.createHmac("sha256", SIGNING_KEY)
.update(`${timestamp}.${path}`)
.digest("hex")
// timingSafeEqual evita que un atacante deduzca la firma midiendo cuánto
// tarda la comparación carácter por carácter.
const received = signatureHeader.replace("sha256=", "")
const valid =
received.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))
if (!valid) return res.status(401).send("Firma inválida")
res.json({
type: "card",
title: "Puntos acumulados",
children: [{ type: "text", value: "1.240 puntos activos", weight: "bold" }],
})
})Referencia
DNA Modules (Storefront)
Lo mismo que dashboard.card, pero para insertar contenido en la tienda pública del comercio.
Cada módulo que declarás en extensions.dna aparece como una opción para que el comercio lo agregue a su tienda desde el Studio (Diseño del inicio). Stockcito te pide el contenido en GET {baseUrl}/stockcito-ui/dna/{key} — mismo esquema de nodos y misma firma que dashboard.card — y lo cachea 60 segundos por comercio, porque acá el que ve el contenido es cualquier visitante anónimo de la tienda, no solo el dueño logueado.
Referencia
Flow Actions
Acciones que el comercio puede invocar automáticamente desde sus automatizaciones (Flow).
Cada acción en extensions.automationActions queda disponible para que el comercio arme una regla tipo "cuando pase X, hacé Y". Cuando se dispara, Stockcito te manda un POST firmado a {baseUrl}/stockcito-actions/{actionKey} con el payload del evento en el body.
POST /stockcito-actions/sumar_puntos
Content-Type: application/json
X-Stockcito-Timestamp: 1735689600
X-Stockcito-Signature: sha256=<hmac del body con tu clave>
{ "orderId": 4821, "total": 15400, "customerId": 92 }Verificá la firma igual que en las extensiones de UI, pero acá el HMAC se calcula sobre timestamp + "." + body (el JSON completo, no un path). Respondé HTTP 2xx si todo salió bien. Si tu respuesta es un error, importa la diferencia: un HTTP 4xx/5xx transitorio (tu servidor caído un momento) hace que Stockcito reintente con backoff; un 2xx que vos mismo interpretás como "falla definitiva" de tu lado no debería devolverse como error HTTP, porque Stockcito no tiene forma de distinguir eso de un problema de red.
Servidor mínimo completo (Node, sin frameworks) — recibe la acción, verifica la firma sobre el body, y responde:
const http = require("http")
const crypto = require("crypto")
const SIGNING_KEY = process.env.STOCKCITO_SIGNING_KEY
http.createServer((req, res) => {
if (req.method !== "POST" || req.url !== "/stockcito-actions/sumar_puntos") {
res.writeHead(404).end()
return
}
let body = ""
req.on("data", (chunk) => { body += chunk })
req.on("end", () => {
const timestamp = req.headers["x-stockcito-timestamp"]
const received = (req.headers["x-stockcito-signature"] || "").replace("sha256=", "")
const expected = crypto
.createHmac("sha256", SIGNING_KEY)
.update(`${timestamp}.${body}`)
.digest("hex")
if (received.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))) {
res.writeHead(401).end("Firma inválida")
return
}
const { orderId, total, customerId } = JSON.parse(body)
// ... tu lógica: sumar puntos, crear el envío, lo que sea ...
console.log(`Pedido ${orderId} de ${customerId} por $${total}`)
res.writeHead(200, { "Content-Type": "application/json" })
res.end(JSON.stringify({ ok: true }))
})
}).listen(3000)Distribución
Marketplace
Cómo publicar tu app para que cualquier comercio la encuentre y la instale con confianza.
Enviá tu app a revisión
Al crear o actualizar tu app en /developers, queda en estado "pendiente" — nunca se publica sola.
Un humano de Stockcito la revisa
Chequea que el manifiesto sea coherente con lo que la app dice hacer. Podés recibir un rechazo con motivo.
Se aprueba un snapshot
Lo que se instala es la versión aprobada (approvedManifest), nunca tu draft en edición — así una versión nueva en revisión no reemplaza silenciosamente lo que ya está instalado.
Le ponés precio (opcional)
Gratis, o un cobro único por instalación vía Mercado Pago Connect — hoy no hay suscripciones recurrentes de apps.
Para cobrar, conectás tu propia cuenta de Mercado Pago desde /developers (el mismo flujo de OAuth que usa un comercio para cobrar en su tienda, aplicado a vos como developer). Cuando alguien compra tu app, se le cobra con tu token de Mercado Pago — Stockcito se queda con un porcentaje como comisión de plataforma — y tu app queda desactivada hasta que el webhook de pago confirma que se acreditó.
Herramientas
SDK de TypeScript
Hay un SDK inicial en TypeScript (packages/sdk-ts) que tipa los recursos de la Public API. Todavía no está publicado en ningún registro público (npm) — por ahora, la forma soportada de integrarte es HTTP + JSON directo, documentado más arriba, que funciona igual desde cualquier lenguaje.
Buenas prácticas
Seguridad
HTTPS público obligatorio
Tu endpoints.baseUrl tiene que resolver a una IP pública. Stockcito rechaza localhost, IPs privadas (10.x, 192.168.x, etc.) y redirects — nunca vamos a llamar a algo dentro de tu red interna.
Verificá siempre la firma
Nunca proceses un webhook, UI fetch o Flow action sin validar X-Stockcito-Signature primero, con tu clave de firma exacta.
Rechazá timestamps viejos
Un timestamp con más de 5 minutos de diferencia respecto a tu reloj es sospechoso de replay — rechazalo aunque la firma sea válida.
Guardá tu token de acceso como secreto
El token de instalación de la Public API es equivalente a una contraseña de esa tienda — tratalo con el mismo cuidado que una clave de base de datos.
Soporte
Preguntas frecuentes
¿Necesito estar en el Marketplace para que un comercio use mi app?▼
No. Cualquier comercio puede instalar tu app pegando la URL de tu manifiesto o el JSON directamente, sin pasar por revisión. El Marketplace es para que comercios que no te conocen te encuentren, con la confianza extra de que Stockcito ya la revisó.
¿Puedo cambiar mi manifiesto sin que se rompa nada instalado?▼
Sí — lo que está instalado sigue usando la versión aprobada hasta que envíes una actualización y esa nueva versión pase su propia revisión.
¿Cómo prueba mi app un comercio de prueba?▼
Instalala igual que cualquier app, pegando el manifiesto directo (no hace falta esperar aprobación del Marketplace para probarla vos mismo).
¿Qué pasa si mi servidor está caído cuando Stockcito me llama?▼
Para UI extensions y DNA modules, simplemente no se muestra ese contenido — no rompe nada del lado del comercio. Para Flow Actions y webhooks, Stockcito reintenta con backoff exponencial durante un tiempo antes de darlo por definitivamente fallido.
