Postman para explorar una API
Cómo organizar requests en Postman mientras exploras una API, cómo convertir esas requests en checks que corren en CI y qué papel tiene Postman frente a las herramientas que usa el agente.
Dónde encaja Postman en este repo
Postman es tu herramienta de exploración manual: guardas requests, las agrupas, les pones variables y les agregas asserts pequeños. El agente trabaja distinto. Para la pierna API de la exploración, el repo le da tres herramientas con un trabajo cada una, y Postman queda como fallback.
| Paso | Herramienta | Para qué |
|---|---|---|
| Leer el contrato | OpenAPI MCP (solo lectura) | Descubrir endpoints y schemas de request y response. |
| Obtener un token | bun run api:login |
Escribe el token en .auth/tokens.env, nunca en un MCP. |
| Ejecutar requests autenticadas | curl |
La ejecución real, con el token del paso anterior. |
| Fallback | Postman (MCP o la app) | Cuando el camino principal no sirve para el caso. |
Ese reparto es la fila [API_TOOL] de la tabla de resolución de herramientas en
.agents/instructions/agent-tool-resolution.md. El detalle del camino principal
está en OpenAPI. Esta página cubre Postman como lo usa
una persona: la app para explorar y el CLI para correr colecciones en un pipeline.
Workspace, collection y folders
Un workspace agrupa colecciones y environments de un proyecto. Dentro, una collection guarda las requests, y los folders las ordenan por dominio o por orden de ejecución.
Workspace: Mi Proyecto API
Collection: Mi Proyecto API
Auth/
Login
Refresh Token
Users/
Get Users
Update Profile
Orders/
List Orders
Create Order
-
Crea el workspace
Workspaces > Create Workspace. Elige Personal o Team según quién va a usarlo.
-
Crea la collection
Collections > New collection. Un nombre por API, no por ticket.
-
Agrega folders y requests
Nombra cada request por lo que prueba:
Create Order (happy path),Get Orders - filtro por fecha. Un nombre comotest1no le dice nada a quien abra la colección después.
Environments y variables
Un environment es un juego de variables por ambiente (local,
staging, ...). Cambias de ambiente con un selector y las mismas requests
apuntan a otro servidor.
| Variable | Valor de ejemplo | Quién la llena |
|---|---|---|
base_url |
https://api.example.com |
Tú, una vez por environment |
api_url |
{{base_url}}/api |
Tú (se arma a partir de otra variable) |
test_email |
qa@example.com |
Tú |
test_password |
(el valor de tu .env) |
Tú, solo en tu máquina |
access_token |
(vacío) | El script del request de login |
user_id |
(vacío) | El script del request de login |
Alcances de variables
Postman tiene tres alcances que usas desde los scripts:
// Environment: cambia con el ambiente seleccionado
pm.environment.set('key', 'value');
pm.environment.get('key');
// Collection: compartida por todas las requests de la colección
pm.collectionVariables.set('key', 'value');
// Global: compartida entre colecciones del workspace
pm.globals.set('key', 'value');
Variables dinámicas
Para datos de prueba que no deben repetirse, Postman genera valores en cada envío:
{
"id": "{{$guid}}",
"timestamp": "{{$timestamp}}",
"random": "{{$randomInt}}",
"email": "{{$randomEmail}}",
"name": "{{$randomFullName}}"
}
Encadenar requests
Una request guarda un valor de su response y la siguiente lo usa en la URL o el body. En el script post-response de la request A:
const response = pm.response.json();
pm.environment.set('product_id', response.id);
Y la URL de la request B: {{api_url}}/products/{{product_id}}.
Autenticación
El patrón más simple: un request Login que guarda el token en el environment, y
el resto de requests lo manda como Bearer.
POST {{base_url}}/auth/login con body raw JSON:
{
"email": "{{test_email}}",
"password": "{{test_password}}"
}
Script post-response del login:
pm.test('Login exitoso', function () {
pm.response.to.have.status(200);
});
const response = pm.response.json();
pm.environment.set('access_token', response.access_token);
pm.environment.set('user_id', response.user.id);
En la pestaña Authorization de la collection elige Bearer Token con el valor
{{access_token}}, y deja cada request en "Inherit auth from parent". Así no
repites el header en cada request. Los nombres de campo (access_token,
user.id) dependen de tu API: revísalos en el schema antes de copiar el script.
Las credenciales de prueba viven en .env. No las escribas en un environment
que se sincroniza con un workspace de equipo ni en un JSON exportado que vaya a git.
Exporta el environment con esos valores vacíos y que cada persona los complete en su
máquina.
Scripts y tests
Cada request tiene dos scripts. El pre-request corre antes de enviar y sirve para preparar
datos o avisar de un problema. El post-response corre al recibir la respuesta y ahí van los
asserts con pm.test. En versiones anteriores de la app el post-response estaba
en una pestaña llamada Tests.
Pre-request: avisar si el token venció
const token = pm.environment.get('access_token');
if (!token) {
console.warn('No hay token. Ejecuta Login primero.');
} else {
try {
const payload = JSON.parse(atob(token.split('.')[1]));
if (Date.now() > payload.exp * 1000) {
console.warn('Token expirado. Ejecuta Refresh Token.');
}
} catch (e) {
// El token no es un JWT: no hay fecha que revisar
}
}
Post-response: status, estructura y valores
pm.test('Status 201 Created', function () {
pm.response.to.have.status(201);
});
pm.test('Estructura correcta', function () {
const r = pm.response.json();
pm.expect(r).to.have.property('id');
pm.expect(r.price).to.be.a('number');
pm.expect(r.tags).to.be.an('array');
});
pm.test('Valores válidos', function () {
const r = pm.response.json();
pm.expect(r.price).to.be.above(0);
pm.expect(r.status).to.be.oneOf(['active', 'pending', 'inactive']);
});
Validar contra un JSON Schema
const schema = {
type: 'object',
required: ['id', 'name', 'price'],
properties: {
id: { type: 'string' },
name: { type: 'string' },
price: { type: 'number' },
},
};
pm.test('Schema válido', function () {
pm.response.to.have.jsonSchema(schema);
});
Permisos
Dos casos que conviene tener en cualquier colección: la request sin token y la request a datos de otro usuario.
// Request sin header Authorization
pm.test('Rechaza sin token', function () {
pm.response.to.have.status(401);
});
// Request con tu token pidiendo datos de otro usuario
pm.test('No expone datos de otro usuario', function () {
if (pm.response.code === 403) return;
pm.response.to.have.status(200);
pm.expect(pm.response.json()).to.be.an('array').that.is.empty;
});
Que la API responda 403 o una lista vacía depende de su diseño. Confírmalo con el equipo antes de fijar el assert a uno de los dos.
Correr la colección entera
El Collection Runner de la app ejecuta todas las requests en orden. Abre el menú de la collection, elige Run, selecciona el environment y, si la API tiene rate limiting, agrega un delay entre requests (100 ms suele alcanzar).
El orden importa cuando las requests dependen unas de otras:
1. Auth/
Login -> guarda access_token
2. Setup/
Get Products -> guarda product_id
3. CRUD/
Create Order -> usa product_id, guarda new_order_id
Get Order
Update Order
Delete Order -> limpia lo que creó
4. Permisos/
Access Other User Data
Postman en CI: Postman CLI y Newman
Para correr una colección en un pipeline hay dos runners. El Postman CLI es el oficial de Postman y el que recomendamos: se autentica con tu API key y puede correr una colección por ID o desde un archivo exportado. Newman es el runner open source de siempre; sirve si no quieres atar el pipeline a una cuenta de Postman.
El boilerplate no incluye colecciones ni un workflow de Postman. Los ejemplos de abajo son el patrón para cuando tu equipo decida versionar una colección.
Postman CLI
# Instalar (Linux / macOS)
curl -o- "https://dl-cli.pstmn.io/install/unix.sh" | sh
# Autenticarse con la API key
postman login --with-api-key "$POSTMAN_API_KEY"
# Correr una colección exportada con su environment
postman collection run postman/collection.json -e postman/staging.environment.json
# O por ID de colección, con reportes JUnit y HTML
postman collection run <collection-id> -e <environment-id> \
-r cli,junit,html \
--reporter-junit-export reports/postman-junit.xml \
--reporter-html-export reports/postman.html
En GitHub Actions, la API key va como secret del repositorio:
name: API Tests (Postman)
on: [pull_request]
jobs:
postman:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Postman CLI
run: curl -o- "https://dl-cli.pstmn.io/install/unix.sh" | sh
- name: Login
run: postman login --with-api-key ${{ secrets.POSTMAN_API_KEY }}
- name: Run collection
run: postman collection run postman/collection.json -e postman/staging.environment.json
Newman
npm install -g newman
newman run postman/collection.json \
-e postman/staging.environment.json \
-r cli,junit \
--reporter-junit-export reports/newman-junit.xml
Newman no necesita cuenta. El reporter HTML no viene incluido: se instala aparte
(newman-reporter-html) y se activa con -r cli,html.
El Postman MCP, a nivel del harness
Postman publica un server MCP remoto, https://mcp.postman.com/mcp, que se
autentica con tu cuenta de Postman. El repo no lo declara en .mcp.json ni en
sus equivalentes: un server remoto cuyo único contenido del lado del proyecto sería una
clave es asunto del harness, no del repo (la lista de esos servers vive en
cli/lib/harness-level-mcps.ts). Lo conectas una vez por máquina y el agente lo
resuelve por capacidad, sea cual sea el prefijo con el que aparezcan sus herramientas.
- Cómo conectarlo
-
Claude Code: un server de usuario (
claude mcp add --scope user) o un conector desde claude.ai. OpenCode: la config global~/.config/opencode/opencode.json. Codex:codex mcp add postman --url https://mcp.postman.com/mcpy despuéscodex mcp login postman(OAuth: el token queda en el almacén de Codex y sirve igual para la CLI y la app de escritorio). Tu API key de Postman, la depostman login, es tuya y no va en el.envdel proyecto. Detalle por harness en Variables de entorno. - Rol
-
Fallback de
[API_TOOL]. El agente lo usa cuando la ruta principal (OpenAPI MCP para el schema,curlpara ejecutar) no cubre el caso, por ejemplo para consultar o correr una colección que el equipo ya mantiene en Postman. - Si falla
-
Una key vacía no detiene el arranque del agente: el error aparece recién en la primera
llamada, como un 401. Revisa la credencial donde la conectaste (el harness, no
.env) y reinicia la sesión del agente, porque las variables se leen al arrancar el MCP.bun run setup:doctorte dice si tu config de usuario declara el server; un conector de claude.ai no deja archivo y lo reporta como no detectable.
Buenas prácticas
-
Variables en vez de valores fijos:
{{base_url}}/api/orders/{{order_id}}, no una URL de producción copiada. - Documenta cada request (pestaña Documentation): qué prueba y qué datos necesita.
-
Si versionas la colección, exporta en formato Collection v2.1 a una carpeta como
postman/, con un environment por ambiente y los secretos vacíos. - Las requests que crean datos terminan con otra que los borra, para que correr la colección dos veces dé el mismo resultado.