Documentación de la API

Sube PDF, cotiza un envío y envíalo solo después de una revisión humana. REST y MCP usan la misma cuenta, los mismos documentos y créditos prepagados.

Contenido

Inicio rápido

Necesitas una cuenta de desarrollador, una clave irk_ con permisos, créditos de envío y un PDF. Estos ejemplos usan Node.js 22 o posterior y una consola POSIX. Todos los identificadores, direcciones y valores de respuesta que se muestran son ilustrativos; reemplaza los marcadores con tus propios datos revisados.

  1. Crea una cuenta, crea una clave con los permisos requeridos y compra créditos. Crear mi cuenta; Administrar claves API; Permisos; Comprar créditos.
  2. Guarda tu carta como letter.pdf. Crea mailing-input.json usando los datos de ejemplo siguientes y reemplaza todos los marcadores de dirección. Se requieren el nombre o empresa del remitente y del destinatario y ambas direcciones postales completas. Datos del envío
  3. Guarda el script siguiente como prepare.mjs. Comprueba tu saldo, sube y registra el PDF, cotiza el envío y escribe reviewed-mailing.json. No envía correspondencia.
  4. Una persona debe revisar el PDF, las direcciones, las opciones, el precio y la autorización para enviar. Solo entonces continúa con Envío y estado. Revisión humana; Envío y estado.
// prepare.mjs — Node.js 22+, no dependencies. Does NOT submit mail.
import { readFile, writeFile } from 'node:fs/promises';

const base = process.env.IRIS_API_URL;
const key = process.env.IRIS_API_KEY;
if (!base || !key || key.includes('REPLACE')) {
  throw new Error('Set IRIS_API_URL and a real server-side IRIS_API_KEY.');
}
async function api(path, body) {
  const response = await fetch(new URL(path, base), {
    method: body === undefined ? 'GET' : 'POST',
    headers: {
      Authorization: 'Bearer ' + key,
      'Content-Type': 'application/json'
    },
    body: body === undefined ? undefined : JSON.stringify(body)
  });
  const result = await response.json();
  if (!response.ok) {
    throw new Error(response.status + ' ' + JSON.stringify(result.error));
  }
  return result.data;
}

const account = await api('/v1/account');
console.log('Account and available US cents:', account);
const pdf = await readFile('letter.pdf');
const upload = await api('/v1/uploads/presign', {
  fileName: 'letter.pdf', sizeBytes: pdf.byteLength, kind: 'letter'
});
const put = await fetch(upload.uploadUrl, {
  method: 'PUT', headers: upload.uploadHeaders, body: pdf
});
if (!put.ok) throw new Error('PDF upload failed: HTTP ' + put.status);

const document = await api('/v1/documents', {
  key: upload.key, fileName: 'letter.pdf', kind: 'letter'
});
console.log('Registered document:', document);
const mailing = JSON.parse(await readFile('mailing-input.json', 'utf8'));
mailing.primaryDocumentId = document.id;
const quote = await api('/v1/mailings/quote', mailing);
await writeFile('reviewed-mailing.json', JSON.stringify(mailing, null, 2));
console.log('Quote in US cents:', JSON.stringify(quote, null, 2));
console.log('STOP: review the PDF, saved payload, price and authority to send.');

Configura el origen de la API y la clave en el entorno de tu servidor local y ejecuta el script de preparación:

export IRIS_API_URL="https://api.irispostalforms.com"
export IRIS_API_KEY="irk_REPLACE_WITH_YOUR_KEY"
# Keep the key in your server environment, never in browser code.
node prepare.mjs

Autenticación y permisos

URL base: https://api.irispostalforms.com. Envía Authorization: Bearer irk_… desde software confiable del servidor. Nunca incluyas una clave API en un repositorio público, código del navegador o instrucciones compartidas. Usa solo los permisos necesarios para cada integración.

Las respuestas REST satisfactorias incluyen los resultados dentro de data; los errores devuelven error.code y error.message. Envía cuerpos JSON con Content-Type: application/json. Todos los importes son números enteros en centavos de dólar estadounidense.

Método y rutaPermisoResultado
GET /v1/accountaccount:readaccountId, creditBalanceCents
POST /v1/uploads/presigndocuments:writeURL privada para PUT
POST /v1/documentsdocuments:writeDocumento inmutable
POST /v1/mailings/quotemailings:readPrecio y desglose
POST /v1/mailingsmailings:writeConfirmación de pedido pagado
GET /v1/mailings/:idmailings:readEstado actual y seguimiento

El ejemplo de preparación necesita account:read, documents:write y mailings:read. El envío también necesita mailings:write. Cada solicitud MCP requiere además account:read.

Cargas y documentos

Sube cada PDF en tres pasos: obtener la firma previa, cargar los bytes con PUT y registrar. El valor key de la carga no es un identificador de documento. Mantén privadas las cargas y las URL prefirmadas.

1. Crea una carga privada

POST /v1/uploads/presign acepta fileName, el sizeBytes exacto y kind. El nombre del PDF puede tener hasta 180 caracteres. El tamaño máximo es de 26 MiB (27,262,976 bytes).

{
  "data": {
    "key": "uploads/api/ACCOUNT_UUID/UPLOAD_UUID.pdf",
    "uploadUrl": "https://storage.example.invalid/presigned-upload",
    "uploadHeaders": {
      "Content-Type": "application/pdf", "If-None-Match": "*"
    },
    "expiresSeconds": 600
  }
}

2. Carga los bytes exactos del PDF con PUT

Usa uploadUrl dentro del plazo expiresSeconds (600 segundos) y envía todos los valores de uploadHeaders devueltos, incluido If-None-Match: *. No envíes tu clave bearer de IRIS al almacenamiento de objetos. Confirma que PUT se completó correctamente antes de registrar. Los errores de almacenamiento no usan el formato JSON de REST.

3. Registra y guarda el identificador del documento

POST /v1/documents acepta el key devuelto, fileName y el mismo kind. HTTP 201 devuelve el documento validado e inmutable:

{
  "data": {
    "id": "11111111-1111-4111-8111-111111111111",
    "kind": "letter", "pages": 1, "sizeBytes": 48211,
    "sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  }
}

Usa data.id como primaryDocumentId (o secondaryDocumentId para tarjetas postales). Volver a registrar la misma clave y tipo devuelve ese documento con HTTP 201 y un accountId adicional. Una versión nueva del PDF necesita una nueva carga y un nuevo identificador de documento.

Dimensiones y límites de páginas PDF

  • letter: tamaño carta de EE. UU., 8.5 × 11 pulgadas (612 × 792 pt), vertical u horizontal. Se pueden registrar hasta 120 páginas; el envío permite 60 páginas PDF a una cara o 120 a doble cara.
  • postcard_4x6: cada cara debe ser una página horizontal de 6.25 × 4.25 pulgadas (450 × 306 pt), incluido el sangrado.
  • postcard_6x9: cada cara debe ser una página horizontal de 9.25 × 6.25 pulgadas (666 × 450 pt), incluido el sangrado.
  • postcard_small (entorno de pruebas de LetterStream): una página de 5.5 × 4.25 pulgadas (396 × 306 pt), con diseño y mensaje dentro de un borde blanco de un cuarto de pulgada. Omite secondaryDocumentId; el proveedor genera la cara de la dirección.
  • postcard_pro (entorno de pruebas de LetterStream): dos archivos de 8.5 × 5.5 pulgadas (612 × 396 pt). El archivo principal es el diseño; el secundario es la cara del mensaje y la dirección. Deja libres las 3.75 pulgadas del extremo derecho de esta cara. La disponibilidad del tamaño con el proveedor se verifica antes del cobro.

Se comprueba cada página, con una tolerancia de 1 pt por dimensión. Los PDF deben abrirse sin contraseña. Las tarjetas postales Lob y LetterStream Pro requieren identificadores de documento distintos para el frente y el reverso, ambos del tipo correspondiente al tamaño elegido. LetterStream Small usa un documento con el diseño y el mensaje en la misma cara. La API no genera el diseño de las tarjetas postales por ti.

Cotizaciones

Guarda esto como mailing-input.json y reemplaza todos los marcadores de dirección. El script de preparación completa primaryDocumentId con el registro y llama a POST /v1/mailings/quote con los datos completos del envío.

{
  "product": "letter",
  "primaryDocumentId": "REPLACED_BY_PREPARE_SCRIPT",
  "sender": {
    "recipient": "REPLACE WITH SENDER NAME",
    "primaryLine": "REPLACE WITH SENDER STREET",
    "city": "REPLACE WITH CITY", "state": "MA", "zipCode": "02108"
  },
  "recipient": {
    "recipient": "REPLACE WITH RECIPIENT NAME",
    "primaryLine": "REPLACE WITH RECIPIENT STREET",
    "city": "REPLACE WITH CITY", "state": "MA", "zipCode": "02109"
  },
  "options": {
    "color": false, "double_sided": true,
    "certified": false, "certified_return_receipt": false
  }
}

Tanto el remitente como el destinatario requieren un nombre o empresa de 1 a 40 caracteres después de eliminar los espacios al inicio y al final; los nombres ausentes o vacíos no superan la validación. Las líneas de dirección permiten 64 caracteres; primaryLine necesita al menos 3. La ciudad permite de 1 a 200 caracteres, el estado requiere dos letras y el ZIP acepta 5 dígitos o ZIP+4, con o sin guion. Los campos opcionales secondaryLine y urbanization, unidos con una coma y un espacio, deben ocupar como máximo 64 caracteres en conjunto.

Las nuevas cotizaciones y los nuevos envíos requieren direcciones de remitente y destinatario en EE. UU.: los 50 estados, DC, AS, GU, MP, PR, VI y los códigos postales militares AA, AE y AP. FM, MH y PW no se aceptan en pedidos nuevos. Omite country para usar el valor nacional predeterminado o proporciona exactamente "US". Se rechazan otros países y los campos no admitidos countryCode, country_code y address_country. Los pedidos pagados anteriormente conservan su cobertura postal aceptada; repetir un envío completado con su clave de idempotencia original no crea otro pedido ni otro cargo.

Las cuatro opciones de carta son obligatorias. El acuse de recibo electrónico requiere certified: true. La impresión a doble cara afecta al número de hojas y a los límites de páginas, pero el precio se calcula por página del PDF.

Consulta el precio calculado por el servidor

Ejemplo de respuesta HTTP 200 para una página de carta en blanco y negro, sin Certified Mail ni acuse de recibo:

El entorno de pruebas de LetterStream también acepta las opciones de carta format (standard, homeowner, flat), paper (W, Y, B, G, O, R, I) y return_envelope (N, 9RWS, 9LWS, 634). Los valores predeterminados son formato estándar, papel blanco y sin sobre de respuesta. Certified Mail, con o sin acuse, requiere el formato estándar. Las cartas admiten de 1 a 5 páginas PDF; los sobres planos grandes, de 1 a 60. Todos permanecen retenidos como trabajos de prueba. Las opciones no predeterminadas no están disponibles en Lob. Los sobres de respuesta no incluyen franqueo de devolución.

{
  "data": {
    "priceCents": 320,
    "breakdown": [
      { "label": "Base service & First-Class postage", "amountCents": 300 },
      { "label": "Black & white printing × 1", "amountCents": 20 }
    ]
  }
}

breakdown contiene label y amountCents para cada concepto. Una cotización no cobra créditos, reserva un precio ni envía correspondencia. Al enviar se recalcula el precio; solicita una cotización nueva si cambias el envío o lo demoras.

Para una tarjeta postal

Repite la firma previa, PUT y el registro para el frente y el reverso usando el mismo tipo de tarjeta postal. Reemplaza ambos identificadores y las direcciones siguientes. Usa size de 4x6 o 6x9; layout de banner, poster o minimal; y font de editorial, modern o handwritten.

El ejemplo siguiente usa tamaños de Lob. El entorno de pruebas de LetterStream acepta, en cambio, 5.5x4.25 (Small, solo archivo principal) y 8.5x5.5 (Pro, dos archivos). Todos los envíos de LetterStream son trabajos de Test Mode retenidos para revisión; no se imprimen ni se envían por correo.

{
  "product": "postcard",
  "primaryDocumentId": "11111111-1111-4111-8111-111111111111",
  "secondaryDocumentId": "44444444-4444-4444-8444-444444444444",
  "sender": {
    "recipient": "REPLACE WITH SENDER NAME",
    "primaryLine": "REPLACE WITH SENDER STREET",
    "city": "REPLACE WITH CITY", "state": "MA", "zipCode": "02108"
  },
  "recipient": {
    "recipient": "REPLACE WITH RECIPIENT NAME",
    "primaryLine": "REPLACE WITH RECIPIENT STREET",
    "city": "REPLACE WITH CITY", "state": "MA", "zipCode": "02109"
  },
  "options": {
    "size": "4x6", "layout": "minimal", "font": "modern",
    "artwork": { "type": "upload" }
  }
}

Estas opciones describen los PDF ya generados. Para diseños del catálogo, usa artwork.type: "destination" con un destinationId admitido por el esquema OpenAPI, en lugar de type: "upload". JSON de OpenAPI 3.1

Envío y estado

Este paso consume créditos y autoriza el procesamiento del envío.

Detente hasta que una persona apruebe los PDF reales, las direcciones, las opciones de impresión, el total cotizado y la intención de enviar. Los datos de ejemplo no son un envío de prueba seguro. El script de preparación se detiene deliberadamente antes de este paso.

Tras la aprobación, usa exactamente el archivo reviewed-mailing.json guardado. Conserva su clave de idempotencia junto con esos datos. El comando siguiente no sobrescribe un archivo de clave existente; usa un directorio de trabajo separado para cada nuevo envío previsto.

# Only AFTER a person approves this exact PDF, payload and quoted price.
# Create and save a key ONCE per intended mailing. Do not repeat on retry.
node --input-type=module -e "import { writeFileSync } from 'node:fs'; writeFileSync('idempotency-key.txt', crypto.randomUUID(), { flag: 'wx' });"

# Retry this command with the SAME saved key and unchanged payload.
curl --fail-with-body --silent --show-error \
  "$IRIS_API_URL/v1/mailings" \
  -H "Authorization: Bearer $IRIS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(cat idempotency-key.txt)" \
  --data-binary @reviewed-mailing.json \
  --output submitted.json

Lee submitted.json. HTTP 201 significa que se creó un pedido nuevo. HTTP 200 significa que se repitió la solicitud completada sin otro cargo. Ejemplo:

{
  "data": {
    "orderId": "22222222-2222-4222-8222-222222222222",
    "priceCents": 320, "balanceCents": 4680, "replay": false,
    "status": "paid",
    "statusUrl": "https://irispostalforms.com/order/22222222-2222-4222-8222-222222222222"
  }
}

balanceCents es el saldo actual, incluso al repetir una solicitud. status: "paid" es una confirmación de creación, no el estado actual de producción o entrega, tampoco al repetir. statusUrl abre la página del pedido para personas en el sitio de consumidores; no es el endpoint REST de consulta periódica.

Consultar el estado actual

# Use data.orderId from a successful submitted.json response, not document.id.
ORDER_ID=$(node --input-type=module -e "import { readFileSync } from 'node:fs'; const r = JSON.parse(readFileSync('submitted.json', 'utf8')); if (!r.data?.orderId) throw new Error('No successful order'); console.log(r.data.orderId);")
curl --fail-with-body --silent --show-error \
  "$IRIS_API_URL/v1/mailings/$ORDER_ID" \
  -H "Authorization: Bearer $IRIS_API_KEY"
{
  "data": {
    "orderId": "22222222-2222-4222-8222-222222222222",
    "product": "letter", "status": "paid", "priceCents": 320,
    "trackingNumber": null, "expectedDeliveryDate": null,
    "createdAt": "2026-09-08T12:00:00.000Z"
  }
}

Los pedidos de la API empiezan en paid. El procesamiento puede pasar por submitted_to_lob, in_production, in_transit y delivered, o informar returned, fulfillment_failed o canceled. Las actualizaciones pueden omitir etapas. Los estados compartidos draft, pending_payment y payment_review no son estados de creación de la API.

trackingNumber y expectedDeliveryDate pueden ser null. Una fecha estimada no garantiza la entrega. Consulta dentro de los límites de solicitudes; no vuelvas a enviar para comprobar un pedido existente.

Errores e idempotencia

Usa una Idempotency-Key única por envío previsto, de hasta 200 caracteres después de eliminar espacios al inicio y al final. Las claves se aplican a toda la cuenta, incluso entre distintas claves API. Reintentar la misma clave con los mismos datos validados devuelve el pedido original; cambiar los datos devuelve HTTP 409.

Si un envío agota el tiempo de espera o su resultado es incierto, conserva los datos y la clave y vuelve a intentarlo sin cambios. No generes una clave nueva para reintentar: podría crear un segundo envío y un segundo cargo. Investiga los conflictos antes de decidir enviar otra vez.

HTTPCódigoPaso siguiente
400invalid_request, idempotency_key_required, document_contract_invalidCorrige los campos, el encabezado o la incompatibilidad entre el PDF y las opciones antes de reintentar.
400Errores de registro de documentosComprueba la validez, el tamaño, las dimensiones y las páginas del PDF, y la propiedad de la carga. Consulta los códigos en OpenAPI.
401api_key_invalidProporciona una clave válida. La respuesta incluye WWW-Authenticate: Bearer.
402insufficient_creditsAñade créditos antes de enviar.
403scope_requiredUsa una clave con el permiso indicado.
404document_not_found, mailing_not_foundComprueba el identificador y la propiedad de la cuenta.
409idempotency_mismatch, mailing_request_failed, mailing_order_missingResuelve la solicitud existente; no crees una clave nueva sin investigar.
415json_requiredLas solicitudes de firma previa requieren Content-Type: application/json.
429rate_limit_exceededEspera la cantidad de segundos indicada en Retry-After.
500internal_errorComprueba el JSON de la solicitud. Si el resultado del envío es incierto, conserva la clave y los datos.

Los límites se aplican a todos los endpoints: 60 solicitudes por clave API por minuto y 120 por IP de cliente por minuto. Cualquiera de ellos puede devolver 429. La API devuelve Retry-After cuando limita una solicitud; no publica encabezados con la cuota restante.

HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json

{"error":{"code":"rate_limit_exceeded","message":"Too many API requests."}}

El registro de documentos puede devolver texto de error del analizador o del almacenamiento como error.code; no trates sus códigos como una lista cerrada. El JSON mal formado actualmente devuelve 500 internal_error en otros endpoints JSON, o 400 en el registro de documentos. Analiza las respuestas de forma defensiva.

MCP

Usa un cliente que admita Streamable HTTP remoto con autenticación. Configura el endpoint y los encabezados siguientes según la documentación de tu cliente. La sintaxis varía: esta es información de conexión, no una configuración universal.

Endpoint: https://api.irispostalforms.com/mcp
Transport: Streamable HTTP
Authorization: Bearer irk_REPLACE_WITH_YOUR_KEY
Accept: application/json, text/event-stream

Inicializa la conexión MCP y descubre las herramientas usando el protocolo. Cada solicitud requiere account:read; las herramientas exigen los permisos adicionales indicados a continuación. Los resultados contienen JSON serializado dentro del contenido de texto MCP, no el contenedor data de REST. Los fallos siguen el manejo de errores de MCP.

HerramientaEntradaPermiso adicional
get-account-balanceSin camposNinguno
create-document-uploadfileName, sizeBytes, kinddocuments:write
register-documentkey, fileName, kinddocuments:write
quote-mailingmailing: datos completos de envío RESTmailings:read
create-mailingidempotencyKey, mailingmailings:write
get-mailing-statusorderIdmailings:read

El cliente aún debe cargar los bytes del PDF mediante PUT a la URL prefirmada con todos los encabezados devueltos antes de llamar a register-document. Cotiza y muestra a una persona el documento exacto, las direcciones y el precio antes de permitir create-mailing. MCP no elimina los requisitos de revisión humana ni de idempotencia.

Revisión humana

Un agente puede preparar documentos y datos, pero una persona debe decidir el envío. Tu aplicación debe colocar el paso de aprobación antes de la solicitud de envío REST o de la llamada MCP create-mailing.

  • Muestra la carta real o ambas caras de la tarjeta postal, no solo los nombres de archivo o resúmenes generados.
  • Confirma las direcciones completas del remitente y del destinatario, las opciones de impresión y el precio cotizado actual.
  • Confirma la autorización para contactar al destinatario, la intención del mensaje, los derechos sobre documentos e imágenes y los requisitos legales aplicables.
  • Confirma los créditos disponibles y conserva los datos aprobados junto con su clave de idempotencia única.

La persona u organización que opera la integración sigue siendo responsable de lo que se envía. Una cotización satisfactoria valida la propiedad del documento y su compatibilidad de impresión; no representa consentimiento, aprobación legal ni una promesa de entrega.

Límites actuales

Límites actuales

Volver al inicio rápido