curl --request POST \
--url https://api.tiendadepuntos.com/external/products \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"name": "Café gratis",
"shortDescription": "Café a elección",
"offers": [
{
"points": 500,
"levelId": 3
}
],
"description": "Un café de especialidad a elección.",
"termsAndConditions": "Válido de lunes a viernes.",
"image": "https://mi-cdn.com/premios/cafe.png",
"isEnabled": true,
"categoryIds": [
7
],
"maxPurchases": 1,
"expirationTermInDays": 30
}
'import requests
url = "https://api.tiendadepuntos.com/external/products"
payload = {
"name": "Café gratis",
"shortDescription": "Café a elección",
"offers": [
{
"points": 500,
"levelId": 3
}
],
"description": "Un café de especialidad a elección.",
"termsAndConditions": "Válido de lunes a viernes.",
"image": "https://mi-cdn.com/premios/cafe.png",
"isEnabled": True,
"categoryIds": [7],
"maxPurchases": 1,
"expirationTermInDays": 30
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'Café gratis',
shortDescription: 'Café a elección',
offers: [{points: 500, levelId: 3}],
description: 'Un café de especialidad a elección.',
termsAndConditions: 'Válido de lunes a viernes.',
image: 'https://mi-cdn.com/premios/cafe.png',
isEnabled: true,
categoryIds: [7],
maxPurchases: 1,
expirationTermInDays: 30
})
};
fetch('https://api.tiendadepuntos.com/external/products', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.tiendadepuntos.com/external/products",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Café gratis',
'shortDescription' => 'Café a elección',
'offers' => [
[
'points' => 500,
'levelId' => 3
]
],
'description' => 'Un café de especialidad a elección.',
'termsAndConditions' => 'Válido de lunes a viernes.',
'image' => 'https://mi-cdn.com/premios/cafe.png',
'isEnabled' => true,
'categoryIds' => [
7
],
'maxPurchases' => 1,
'expirationTermInDays' => 30
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.tiendadepuntos.com/external/products"
payload := strings.NewReader("{\n \"name\": \"Café gratis\",\n \"shortDescription\": \"Café a elección\",\n \"offers\": [\n {\n \"points\": 500,\n \"levelId\": 3\n }\n ],\n \"description\": \"Un café de especialidad a elección.\",\n \"termsAndConditions\": \"Válido de lunes a viernes.\",\n \"image\": \"https://mi-cdn.com/premios/cafe.png\",\n \"isEnabled\": true,\n \"categoryIds\": [\n 7\n ],\n \"maxPurchases\": 1,\n \"expirationTermInDays\": 30\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.tiendadepuntos.com/external/products")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Café gratis\",\n \"shortDescription\": \"Café a elección\",\n \"offers\": [\n {\n \"points\": 500,\n \"levelId\": 3\n }\n ],\n \"description\": \"Un café de especialidad a elección.\",\n \"termsAndConditions\": \"Válido de lunes a viernes.\",\n \"image\": \"https://mi-cdn.com/premios/cafe.png\",\n \"isEnabled\": true,\n \"categoryIds\": [\n 7\n ],\n \"maxPurchases\": 1,\n \"expirationTermInDays\": 30\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tiendadepuntos.com/external/products")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Café gratis\",\n \"shortDescription\": \"Café a elección\",\n \"offers\": [\n {\n \"points\": 500,\n \"levelId\": 3\n }\n ],\n \"description\": \"Un café de especialidad a elección.\",\n \"termsAndConditions\": \"Válido de lunes a viernes.\",\n \"image\": \"https://mi-cdn.com/premios/cafe.png\",\n \"isEnabled\": true,\n \"categoryIds\": [\n 7\n ],\n \"maxPurchases\": 1,\n \"expirationTermInDays\": 30\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"status": 201,
"message": "Request was successful",
"data": {
"id": 771,
"name": "Café gratis",
"description": "Un café de especialidad a elección.",
"shortDescription": "Café a elección",
"image": "https://tdp-products.s3.amazonaws.com/771.png",
"isEnabled": true,
"stock": {
"controlled": true,
"available": 12
},
"offers": [
{
"productOfferId": 4412,
"points": 500,
"levelId": 3,
"levelName": "Nivel 2"
}
],
"categories": [
{
"id": 7,
"name": "Cafetería"
}
],
"createdAt": "2026-02-14T12:30:00.000Z",
"updatedAt": "2026-06-01T09:15:00.000Z",
"maxPurchases": null
}
}{
"statusCode": 400,
"timestamp": "2026-07-31T14:05:12.431Z",
"path": "/external/tags/add",
"method": "POST",
"message": "Error de validación",
"errors": [
{}
],
"data": {}
}{
"statusCode": 401,
"timestamp": "2026-07-31T14:05:12.431Z",
"path": "/external/tags/add",
"method": "POST",
"message": "Api key is invalid",
"errors": [
{}
],
"data": {}
}{
"statusCode": 404,
"timestamp": "2026-07-31T14:05:12.431Z",
"path": "/external/tags/add",
"method": "POST",
"message": "Client not found",
"errors": [
{}
],
"data": {}
}Crear un premio
Crea un premio en el catálogo del comercio.
Cada premio necesita al menos una oferta en offers: es el precio en puntos, y podés poner uno distinto por nivel. Los levelId salen de GET /external/levels; una oferta sin levelId es el precio para los clientes sin nivel.
El premio se crea disponible en todas las sucursales. Si mandás stock.controlled: true con available: 0, el premio nace deshabilitado.
curl --request POST \
--url https://api.tiendadepuntos.com/external/products \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"name": "Café gratis",
"shortDescription": "Café a elección",
"offers": [
{
"points": 500,
"levelId": 3
}
],
"description": "Un café de especialidad a elección.",
"termsAndConditions": "Válido de lunes a viernes.",
"image": "https://mi-cdn.com/premios/cafe.png",
"isEnabled": true,
"categoryIds": [
7
],
"maxPurchases": 1,
"expirationTermInDays": 30
}
'import requests
url = "https://api.tiendadepuntos.com/external/products"
payload = {
"name": "Café gratis",
"shortDescription": "Café a elección",
"offers": [
{
"points": 500,
"levelId": 3
}
],
"description": "Un café de especialidad a elección.",
"termsAndConditions": "Válido de lunes a viernes.",
"image": "https://mi-cdn.com/premios/cafe.png",
"isEnabled": True,
"categoryIds": [7],
"maxPurchases": 1,
"expirationTermInDays": 30
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'Café gratis',
shortDescription: 'Café a elección',
offers: [{points: 500, levelId: 3}],
description: 'Un café de especialidad a elección.',
termsAndConditions: 'Válido de lunes a viernes.',
image: 'https://mi-cdn.com/premios/cafe.png',
isEnabled: true,
categoryIds: [7],
maxPurchases: 1,
expirationTermInDays: 30
})
};
fetch('https://api.tiendadepuntos.com/external/products', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.tiendadepuntos.com/external/products",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Café gratis',
'shortDescription' => 'Café a elección',
'offers' => [
[
'points' => 500,
'levelId' => 3
]
],
'description' => 'Un café de especialidad a elección.',
'termsAndConditions' => 'Válido de lunes a viernes.',
'image' => 'https://mi-cdn.com/premios/cafe.png',
'isEnabled' => true,
'categoryIds' => [
7
],
'maxPurchases' => 1,
'expirationTermInDays' => 30
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.tiendadepuntos.com/external/products"
payload := strings.NewReader("{\n \"name\": \"Café gratis\",\n \"shortDescription\": \"Café a elección\",\n \"offers\": [\n {\n \"points\": 500,\n \"levelId\": 3\n }\n ],\n \"description\": \"Un café de especialidad a elección.\",\n \"termsAndConditions\": \"Válido de lunes a viernes.\",\n \"image\": \"https://mi-cdn.com/premios/cafe.png\",\n \"isEnabled\": true,\n \"categoryIds\": [\n 7\n ],\n \"maxPurchases\": 1,\n \"expirationTermInDays\": 30\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.tiendadepuntos.com/external/products")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Café gratis\",\n \"shortDescription\": \"Café a elección\",\n \"offers\": [\n {\n \"points\": 500,\n \"levelId\": 3\n }\n ],\n \"description\": \"Un café de especialidad a elección.\",\n \"termsAndConditions\": \"Válido de lunes a viernes.\",\n \"image\": \"https://mi-cdn.com/premios/cafe.png\",\n \"isEnabled\": true,\n \"categoryIds\": [\n 7\n ],\n \"maxPurchases\": 1,\n \"expirationTermInDays\": 30\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tiendadepuntos.com/external/products")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Café gratis\",\n \"shortDescription\": \"Café a elección\",\n \"offers\": [\n {\n \"points\": 500,\n \"levelId\": 3\n }\n ],\n \"description\": \"Un café de especialidad a elección.\",\n \"termsAndConditions\": \"Válido de lunes a viernes.\",\n \"image\": \"https://mi-cdn.com/premios/cafe.png\",\n \"isEnabled\": true,\n \"categoryIds\": [\n 7\n ],\n \"maxPurchases\": 1,\n \"expirationTermInDays\": 30\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"status": 201,
"message": "Request was successful",
"data": {
"id": 771,
"name": "Café gratis",
"description": "Un café de especialidad a elección.",
"shortDescription": "Café a elección",
"image": "https://tdp-products.s3.amazonaws.com/771.png",
"isEnabled": true,
"stock": {
"controlled": true,
"available": 12
},
"offers": [
{
"productOfferId": 4412,
"points": 500,
"levelId": 3,
"levelName": "Nivel 2"
}
],
"categories": [
{
"id": 7,
"name": "Cafetería"
}
],
"createdAt": "2026-02-14T12:30:00.000Z",
"updatedAt": "2026-06-01T09:15:00.000Z",
"maxPurchases": null
}
}{
"statusCode": 400,
"timestamp": "2026-07-31T14:05:12.431Z",
"path": "/external/tags/add",
"method": "POST",
"message": "Error de validación",
"errors": [
{}
],
"data": {}
}{
"statusCode": 401,
"timestamp": "2026-07-31T14:05:12.431Z",
"path": "/external/tags/add",
"method": "POST",
"message": "Api key is invalid",
"errors": [
{}
],
"data": {}
}{
"statusCode": 404,
"timestamp": "2026-07-31T14:05:12.431Z",
"path": "/external/tags/add",
"method": "POST",
"message": "Client not found",
"errors": [
{}
],
"data": {}
}Authorizations
API key del comercio. Se genera desde el panel de Tienda de Puntos, en Configuración → Integraciones.
Body
Nombre del premio.
"Café gratis"
Descripción corta. Es la que se ve en el listado de premios de la app.
"Café a elección"
Precio en puntos por nivel. Mandá una oferta por cada nivel que quieras cubrir; el productOfferId que devuelve la respuesta es el que consume POST /external/exchange/product.
Show child attributes
Show child attributes
Descripción larga del premio.
"Un café de especialidad a elección."
Términos y condiciones del canje.
"Válido de lunes a viernes."
URL pública de la imagen del premio. Tienda de Puntos no descarga la imagen: guarda la URL tal cual. Si se omite, el premio queda sin imagen.
"https://mi-cdn.com/premios/cafe.png"
Si es false, el premio no aparece en el catálogo ni se puede canjear. Si el stock arranca en 0, el premio queda deshabilitado aunque mandes true.
true
IDs de las categorías de premios del comercio a las que pertenece.
[7]
Control de stock del premio. Si se omite, el premio queda sin control de stock.
Show child attributes
Show child attributes
Máximo de canjes por cliente a lo largo de la vida del premio. Omitilo (o mandá null) para no poner tope.
1
Días que tiene el cliente para usar el cupón de canje. Omitilo para que el canje no venza.
30

