Documentación de la API
Integra tus aplicaciones con el marketplace: lee tu cuenta y artículos, obtén tus ventas para paneles de control personalizados y verifica los códigos de compra del comprador desde tu propio servidor de licencias.
curl https://sellmycode.net/api
Autenticación
Cada solicitud necesita un token de acceso personal. Crea tokens con nombre y ámbito en tu espacio de trabajo en Configuración → Clave API — cada aplicación obtiene su propio token con solo los permisos que necesita, y puedes revocar cualquiera de ellos en cualquier momento.
curl https://sellmycode.net/api/account/details \ -H "Authorization: Bearer YOUR_API_KEY"
curl https://sellmycode.net/api/account/details \ -H "X-Api-Key: YOUR_API_KEY"
Errores y límites
| Código | Significado |
|---|---|
| 200 | éxito |
| 400 | Error de validación: falta un parámetro requerido o está mal formado |
| 401 | Clave API inválida o faltante |
| 404 | Recurso no encontrado (también se devuelve para un código de compra no válido) |
| 429 | Límite de peticiones excedido: espera y vuelve a intentarlo (60 peticiones por minuto) |
{
"status": "error",
"msg": "Invalid request"
}
GETDetalles de la cuenta
Devuelve el perfil de la cuenta propietaria de la clave API.
curl https://sellmycode.net/api/account/details \ -H "Authorization: Bearer YOUR_API_KEY"
GETTodos los artículos
Todos tus artículos aprobados, los más recientes primero. Solo autores.
curl https://sellmycode.net/api/items/all \ -H "Authorization: Bearer YOUR_API_KEY"
GETArtículo individual
Uno de tus artículos aprobados por su ID numérico.
| Parámetro | Tipo | Descripción |
|---|---|---|
| item_id Requerido | integer | El ID del artículo (mostrado en la lista de artículos de tu espacio de trabajo) |
curl "https://sellmycode.net/api/items/item?item_id=123" \ -H "Authorization: Bearer YOUR_API_KEY"
GETVentas
Tus ventas, las más recientes primero: crea paneles de ingresos o sincroniza pedidos con tus propias herramientas. Paginado.
| Parámetro | Tipo | Descripción |
|---|---|---|
| from opcional | date | Y-m-d — solo ventas en o después de esta fecha |
| to opcional | date | Y-m-d — solo ventas en o antes de esta fecha |
| item_id opcional | integer | filtrar por uno de tus artículos |
| status opcional | string | active | refunded | cancelled | held |
| per_page opcional | integer | por defecto 25, máximo 50 |
| page opcional | integer | número de página |
curl "https://sellmycode.net/api/sales?from=2026-01-01&status=active&per_page=25" \ -H "Authorization: Bearer YOUR_API_KEY"
{
"status": "success",
"pagination": { "page": 1, "per_page": 25, "total": 132, "last_page": 6 },
"sales": [
{
"id": 981,
"purchase_code": "8f14e45f-ce95-41d8-a2b6-72e5f13d81a7",
"item": { "id": 123, "name": "My Theme" },
"buyer": "johndoe",
"license_type": "Regular",
"price": 29.0,
"fee": 5.8,
"earning": 23.2,
"currency": "USD",
"sale_status": "active",
"cleared": true,
"date": "2026-07-01T09:30:00+00:00"
}
]
}
GETMis compras
Tus propias compras como comprador, las más recientes primero: consulta licencias y ventanas de descarga desde tus herramientas. Paginado.
| Parámetro | Tipo | Descripción |
|---|---|---|
| status opcional | string | active | refunded | cancelled | held |
| per_page opcional | integer | por defecto 25, máximo 50 |
| page opcional | integer | número de página |
curl "https://sellmycode.net/api/purchases?status=active" \ -H "Authorization: Bearer YOUR_API_KEY"
{
"status": "success",
"pagination": { "page": 1, "per_page": 25, "total": 3, "last_page": 1 },
"purchases": [
{
"purchase_code": "8f14e45f-ce95-41d8-a2b6-72e5f13d81a7",
"item": { "id": 123, "name": "My Theme", "url": "https://sellmycode.net/item/my-theme/123" },
"license_type": "Regular",
"price": 29.0,
"currency": "USD",
"purchase_status": "active",
"download_expiry_at": "2027-01-01T00:00:00+00:00",
"download_expired": false,
"date": "2026-07-01T09:30:00+00:00"
}
]
}
GETSaldo
Una instantánea de solo lectura de tus monederos: ganancias retirables, ganancias aún en período de compensación y Crédito de la tienda. Opcionalmente, incluye tu historial de extractos.
| Parámetro | Tipo | Descripción |
|---|---|---|
| statements opcional | boolean | 1 para incluir extractos recientes |
| wallet opcional | string | balance | store_credit — filtrar extractos por monedero |
| per_page opcional | integer | por defecto 25, máximo 50 |
curl "https://sellmycode.net/api/balance?statements=1&wallet=balance" \ -H "Authorization: Bearer YOUR_API_KEY"
{
"status": "success",
"currency": "USD",
"wallets": {
"balance": 1250.75,
"pending_balance": 89.4,
"store_credit": 12.5
},
"pagination": { "page": 1, "per_page": 25, "total": 57, "last_page": 3 },
"statements": [
{
"id": 4021,
"title": "[Sale] #981 (My Theme)",
"wallet": "balance",
"type": "credit",
"amount": 29.0,
"total": 23.2,
"date": "2026-07-01T09:30:05+00:00"
}
]
}
POSTValidación de compra
Verifica un código de compra que te da un comprador, el núcleo de cualquier sistema de licencias. Devuelve los detalles de la compra cuando el código pertenece a UNO de TUS artículos y aún está activo; de lo contrario, 404.
| Parámetro | Tipo | Descripción |
|---|---|---|
| purchase_code Requerido | string | El código del comprador (mostrado en su página de compras y certificado de licencia) |
curl -X POST https://sellmycode.net/api/purchases/validation \ -H "Authorization: Bearer YOUR_API_KEY" \ -d "purchase_code=BUYER_PURCHASE_CODE"
// license check from your app / plugin $ch = curl_init('https://sellmycode.net/api/purchases/validation'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Authorization: Bearer YOUR_API_KEY'], CURLOPT_POSTFIELDS => http_build_query(['purchase_code' => $code]), ]); $res = json_decode(curl_exec($ch), true); $valid = ($res['status'] ?? '') === 'success';
// Node.js 18+ (built-in fetch) const res = await fetch('https://sellmycode.net/api/purchases/validation', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ purchase_code: code }), }); const data = await res.json(); const valid = res.ok && data.status === 'success';
# Python 3 + requests import requests res = requests.post( 'https://sellmycode.net/api/purchases/validation', headers={'Authorization': 'Bearer YOUR_API_KEY'}, data={'purchase_code': code}, timeout=10, ) valid = res.status_code == 200 and res.json().get('status') == 'success'
{
"status": "success",
"buyer": "johndoe",
"item": {
"purchase_code": "8f14e45f-ce95-41d8-a2b6-72e5f13d81a7",
"license_type": "Regular",
"price": 29.0,
"currency": "USD",
"item": { "…item fields…" },
"supported_until": "2026-12-01T00:00:00+00:00",
"download_expiry": "2026-08-01T00:00:00+00:00",
"downloaded": true,
"date": "2026-07-01T09:30:00+00:00"
}
}
GETCatálogo Público
Navega y busca en todo el catálogo aprobado — abierto a TODAS las cuentas, comprador o autor. Construye rastreadores de precios, exhibiciones de portafolios o paneles de categorías. Devuelve solo datos de la tienda: precios orientados al comprador (descuentos incluidos), valoraciones y recuentos de ventas — nunca archivos, enlaces de descarga o identidades de compradores.
| Parámetro | Tipo | Descripción |
|---|---|---|
| search opcional | string | búsqueda de texto completo en nombres de artículos y etiquetas |
| category opcional | string | un slug de categoría (ver el endpoint de categorías) |
| sort opcional | string | latest (predeterminado) | popular | rating |
| per_page opcional | integer | por defecto 25, máximo 50 |
| page opcional | integer | número de página |
curl "https://sellmycode.net/api/catalog/items?search=woocommerce&sort=popular" \ -H "Authorization: Bearer YOUR_API_KEY"
{
"status": "success",
"pagination": { "page": 1, "per_page": 25, "total": 64, "last_page": 3 },
"items": [
{
"id": 123,
"name": "My Theme",
"url": "https://sellmycode.net/item/my-theme/123",
"thumbnail": "https://sellmycode.net/files/thumbnails/…",
"category": { "id": 4, "name": "WordPress", "slug": "wordpress" },
"author": "janedoe",
"price": { "regular": 29.0, "extended": 145.0 },
"currency": "USD",
"rating": { "average": 4.8, "count": 36 },
"version": "2.1.0",
"updated_at": "2026-07-10T08:00:00+00:00",
"published_at": "2026-01-05T12:00:00+00:00"
}
]
}
Detalle público completo de un artículo aprobado — los campos de la lista más descripción, vistas previas de medios, lista de archivos incluidos y marco.
curl https://sellmycode.net/api/catalog/items/123 \ -H "Authorization: Bearer YOUR_API_KEY"
Todas las categorías con sus recuentos de artículos aprobados — usa el slug como filtro de categoría anterior.
{
"status": "success",
"categories": [
{ "id": 4, "name": "WordPress", "slug": "wordpress", "url": "https://sellmycode.net/categories/wordpress", "items_count": 128 }
]
}
Activación de licencia
Para software que se conecta con el servidor: scripts PHP, aplicaciones de escritorio o móviles. Su instalación envía el código de compra y su dominio; nosotros confirmamos la licencia y contamos en cuántos sitios se ejecuta, para que no tenga que alojar un servidor de licencias usted mismo.
| Parámetro | Tipo | Descripción |
|---|---|---|
| purchase_code | string | el código que recibió el comprador |
| domain | string | el sitio que ejecuta el software; el esquema, www y la ruta se eliminan automáticamente |
curl "https://sellmycode.net/api/license/activate" \ -H "Content-Type: application/json" \ -d '{"purchase_code":"d71c21a3-...","domain":"customer-site.com"}'
{
"status": "success",
"code": "activated",
"domain": "customer-site.com",
"license_type": "Regular",
"activation_limit": 2,
"activations_used": 1,
"updates": { "active": true, "until": "2027-01-30T10:00:00+00:00" }
}
| Código de error | HTTP | Significado |
|---|---|---|
| invalid_code | 403 | código de compra desconocido, reembolsado o cancelado |
| activation_limit_reached | 403 | todos los sitios permitidos están en uso; desactive uno primero |
| not_activated | 403 | este dominio nunca se ha activado (o ha sido liberado) |
| invalid_domain | 400 | no se pudo analizar el dominio |
| rate_limited | 429 | demasiadas llamadas desde esta IP |
Actualizaciones del artículo (Toolkit)
Permita que un sitio de WordPress mantenga actualizados los temas y plugins que compró aquí. Indíquenos qué paquetes están instalados, reciba los que tienen una versión más reciente y luego obtenga el ZIP instalable. Esto es lo que usa el plugin SellMyCode Toolkit; solo necesita estos endpoints si está creando su propio actualizador.
¿Usas WordPress? Sáltate el código
El plugin SellMyCode Toolkit ya vincula estos endpoints a las pantallas habituales de actualización de Temas y Plugins. Instálalo, pega un token y los artículos comprados se actualizarán como cualquier otra cosa en WordPress.
POSTBuscar actualizaciones
| Parámetro | Tipo | Descripción |
|---|---|---|
| packages | array | paquetes instalados, hasta 100 por llamada |
| packages[].slug | string | el nombre de la carpeta del tema o plugin |
| packages[].type | string | theme | plugin |
| packages[].version opcional | string | la versión instalada actualmente |
curl "https://sellmycode.net/api/updates/check" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"packages":[{"slug":"my-theme","type":"theme","version":"1.4.0"}]}'
{
"status": "success",
"checked": 1,
"updates": [
{
"item_id": 123,
"slug": "my-theme",
"type": "theme",
"name": "My Theme",
"installed_version": "1.4.0",
"new_version": "1.6.2",
"changelog": { "version": "1.6.2", "body": "Fixed ..." },
"can_download": true,
"reason": null,
"update_window_ends_at": "2027-01-30T10:00:00+00:00",
"download_url": "https://sellmycode.net/api/updates/download/123",
"renew_url": null
}
]
}
GETDescargar el paquete instalable
Responde con una redirección 302 a una URL de almacenamiento de corta duración (válida por unos minutos). Siga las redirecciones y guarde el ZIP: solo contiene el tema o plugin, listo para instalar.
curl -L -o update.zip "https://sellmycode.net/api/updates/download/123" \ -H "Authorization: Bearer YOUR_API_KEY"
| Código de error | HTTP | Significado |
|---|---|---|
| not_purchased | 403 | esta cuenta no compró el artículo |
| update_window_expired | 403 | el soporte de actualizaciones ha finalizado — renew_url se incluye en la respuesta |
| no_installable_file | 404 | el autor no ha proporcionado un paquete instalable para este artículo |
| item_removed | 410 | el artículo ya no está disponible |
Webhooks de venta
En lugar de sondear el endpoint de ventas, registra una URL HTTPS y te enviaremos un evento JSON firmado en el momento en que algo suceda. Administra los endpoints en tu espacio de trabajo en Configuración → Clave de API → Webhooks (hasta 5, cada uno con su propio secreto de firma que se muestra una vez en la creación).
| Evento | Enviado cuando |
|---|---|
| sale.created | Se vende uno de tus artículos, paquetes o cursos |
| sale.refunded | Una de tus ventas es reembolsada o cancelada — revoca la licencia de tu lado |
Entrega
Cada entrega es un HTTP POST con un cuerpo JSON. Responde con cualquier estado 2xx en 10 segundos; realiza trabajos pesados asincrónicamente. Las entregas fallidas se reintentan 3 veces (después de 1 minuto, 15 minutos y 1 hora). Después de 10 fallos consecutivos — o una respuesta 410 Gone — el endpoint se deshabilita automáticamente y se te notifica.
| Encabezado | Descripción |
|---|---|
| X-SMC-Event | El nombre del evento, p. ej. sale.created |
| X-SMC-Delivery | ID de entrega único (UUID) — desduplica los reintentos con él |
| X-SMC-Signature | t=<unix>,v1=<hex> — Firma HMAC-SHA256 (ver abajo) |
{
"id": "1c2f6a7e-9d31-4c3e-8f5b-2d9f8f4f1a10",
"event": "sale.created",
"created_at": "2026-07-18T10:30:00+00:00",
"data": {
"sale_id": 981,
"purchase_code": "8f14e45f-ce95-41d8-a2b6-72e5f13d81a7",
"type": "item",
"item": { "id": 123, "name": "My Theme" },
"buyer": "johndoe",
"license_type": "Regular",
"price": 29.0,
"earning": 23.2,
"currency": "USD",
"date": "2026-07-18T10:30:00+00:00"
}
}
Verificando la firma
Calcula HMAC-SHA256 sobre la cadena "timestamp.rawBody" con tu secreto de firma y compáralo (tiempo constante) con el valor v1. Rechaza las entregas cuya marca de tiempo sea anterior a ~5 minutos para evitar repeticiones.
$secret = 'whsec_...'; // from Settings → API Key → Webhooks $body = file_get_contents('php://input'); $header = $_SERVER['HTTP_X_SMC_SIGNATURE'] ?? ''; parse_str(str_replace(',', '&', $header), $sig); // ['t' => ..., 'v1' => ...] $expected = hash_hmac('sha256', $sig['t'] . '.' . $body, $secret); if (!hash_equals($expected, $sig['v1'] ?? '') || abs(time() - (int) $sig['t']) > 300) { http_response_code(400); exit; } http_response_code(200); // ack fast, process async
// Express: use express.raw() so the RAW body is available for the HMAC const crypto = require('crypto'); const SECRET = 'whsec_...'; // from Settings → API Key → Webhooks app.post('/webhooks/sellmycode', express.raw({ type: 'application/json' }), (req, res) => { const header = req.get('X-SMC-Signature') || ''; const sig = Object.fromEntries(header.split(',').map(p => p.split('='))); const expected = crypto.createHmac('sha256', SECRET) .update(sig.t + '.' + req.body.toString('utf8')).digest('hex'); const fresh = Math.abs(Date.now() / 1000 - Number(sig.t)) < 300; const valid = sig.v1 && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig.v1)); if (!valid || !fresh) return res.sendStatus(400); const event = JSON.parse(req.body); // { id, event, created_at, data } res.sendStatus(200); // ack fast, process async });
# Flask — request.get_data() is the RAW body needed for the HMAC import hmac, hashlib, time from flask import Flask, request SECRET = 'whsec_...' # from Settings → API Key → Webhooks @app.route('/webhooks/sellmycode', methods=['POST']) def webhook(): header = request.headers.get('X-SMC-Signature', '') sig = dict(p.split('=', 1) for p in header.split(',') if '=' in p) body = request.get_data() # bytes, unparsed expected = hmac.new( SECRET.encode(), (sig.get('t', '') + '.').encode() + body, hashlib.sha256 ).hexdigest() fresh = abs(time.time() - float(sig.get('t', 0))) < 300 if not (hmac.compare_digest(expected, sig.get('v1', '')) and fresh): return '', 400 event = request.get_json() # { id, event, created_at, data } return '', 200 # ack fast, process async
# Rails / Rack — request.body.read is the RAW body needed for the HMAC SECRET = 'whsec_...' # from Settings → API Key → Webhooks def webhook header = request.headers['X-SMC-Signature'].to_s sig = header.split(',').to_h { |p| p.split('=', 2) } body = request.body.read expected = OpenSSL::HMAC.hexdigest('SHA256', SECRET, "#{sig['t']}.#{body}") fresh = (Time.now.to_i - sig['t'].to_i).abs < 300 valid = sig['v1'] && ActiveSupport::SecurityUtils.secure_compare(expected, sig['v1']) return head :bad_request unless valid && fresh event = JSON.parse(body) # { id, event, created_at, data } head :ok # ack fast, process async end