API Pública de Sourced v1
La API de Sourced permite que los sistemas ERP (como Calipso, SAP, JD Edwards) se integren directamente con la plataforma de compras de Sourced. Úsala para:
- Enviar requisiciones de compra desde tu ERP a Sourced
- Obtener órdenes de compra de vuelta en tu ERP una vez adjudicadas
- Verificar qué requisiciones ya fueron sincronizadas
- Monitorear el estado de tus requisiciones
La API sigue las convenciones REST, utiliza JSON para los cuerpos de solicitud y respuesta, y se autentica mediante API keys.
Autenticación
Todos los endpoints (excepto health check) requieren una API key enviada en el header X-API-Key.
Las API keys se crean en el panel de administración de Sourced en Configuración → API Keys. Cada key se muestra solo una vez al crearla. Guardala de forma segura.
curl -H "X-API-Key: sk_live_abc123..." \
https://api.gosourced.ai/api/v1/requisitionsURL Base
| Ambiente | URL Base |
|---|---|
| Producción | https://api.gosourced.ai |
| Desarrollo | https://api-dev.gosourced.ai |
Todos los endpoints tienen el prefijo /api/v1.
Límites de tasa
Todos los endpoints están limitados a 60 solicitudes por minuto por endpoint. Exceder este límite devuelve una respuesta 429 Too Many Requests.
Para operaciones masivas, usá el endpoint de importación por lotes (POST /requisitions) que acepta hasta 100 requisiciones por solicitud.
Errores
La API utiliza códigos de estado HTTP estándar. Los errores devuelven un cuerpo JSON con un campo detail.
{
"detail": "Invalid or revoked API key."
}| Estado | Significado |
|---|---|
| 200 | Éxito |
| 400 | Solicitud inválida - error de validación o cuerpo malformado |
| 401 | No autorizado - API key faltante o inválida |
| 404 | No encontrado - el recurso no existe o no es accesible |
| 422 | Entidad no procesable - el cuerpo de la solicitud no pasó la validación del esquema |
| 429 | Demasiadas solicitudes - límite de tasa excedido |
| 500 | Error interno del servidor - error inesperado de nuestro lado |
Ejemplos de errores
{
"detail": "Invalid or revoked API key."
}{
"detail": [
{
"loc": ["body", "requisitions", 0, "items", 0, "quantity"],
"msg": "Input should be greater than 0",
"type": "greater_than"
}
]
}{
"success": false,
"created_count": 2,
"updated_count": 0,
"skipped_count": 0,
"error_count": 1,
"errors": [
{
"index": 2,
"external_id": "REQ-2026-0099",
"error": "Failed to process requisition: duplicate external_line_id within items"
}
],
"requisition_ids": [1234, 1235]
}Nota: el endpoint POST /requisitions devuelve 200 incluso con fallos parciales. Siempre verificá el campo success y el array errors para detectar problemas por requisición.
Health Check
/api/v1/healthVerificar que la API esté accesible. No requiere autenticación.
curl https://api.gosourced.ai/api/v1/health{
"status": "ok",
"api": "v1"
}Subir un archivo (Presigned)
/api/v1/files/presignAPI Key requeridaObtené una URL prefirmada para subir un archivo (un manifiesto de requisición o un adjunto) directo a S3. Los bytes no pasan por la API, así que las especificaciones técnicas pesadas de licitaciones suben sin chocar los límites de tamaño.
Flujo de dos pasos (por archivo)
- Llamá a este endpoint con el nombre del archivo para recibir un upload_url y los campos del formulario.
- Hacé un POST del archivo como multipart/form-data a upload_url, incluyendo primero todos los campos de fields y luego un campo file con los bytes.
Cuerpo del request
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| filename | string | requerido | Nombre original del archivo (ej. 'SOL_106177_LINE_001_SEQ010_adjunto.docx'). Se eliminan los componentes de ruta. |
| content_type | string | opcional | Tipo MIME. Si se omite, se acepta cualquier tipo. |
curl -X POST https://api.gosourced.ai/api/v1/files/presign \
-H "X-API-Key: sk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"filename": "SOL_106177_LINE_001_SEQ010_adjunto.docx",
"content_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
}'{
"upload_url": "https://s3.amazonaws.com/your-bucket",
"fields": {
"key": "erp-inbound/42/SOL_106177_LINE_001_SEQ010_adjunto.docx",
"Content-Type": "application/vnd.openxmlformats-...",
"policy": "eyJleHBpcmF0aW9uIjoi...",
"x-amz-signature": "a1b2c3..."
},
"key": "erp-inbound/42/SOL_106177_LINE_001_SEQ010_adjunto.docx",
"max_file_size": 524288000,
"expires_in": 600
}Campos de la respuesta
- upload_url - la URL de S3 a la que se hace el POST del archivo.
- fields - campos del formulario que deben incluirse en el POST multipart, antes del campo file.
- key - la clave bajo la que se guardará el archivo.
- max_file_size - tamaño máximo permitido en bytes (lo aplica S3).
- expires_in - segundos hasta que vence la URL prefirmada.
# Use upload_url + every key in "fields", then the file (last).
curl -X POST "$UPLOAD_URL" \
-F "key=erp-inbound/42/SOL_106177_LINE_001_SEQ010_adjunto.docx" \
-F "Content-Type=application/vnd.openxmlformats-..." \
-F "policy=eyJleHBpcmF0aW9uIjoi..." \
-F "x-amz-signature=a1b2c3..." \
-F "file=@SOL_106177_LINE_001_SEQ010_adjunto.docx"
# HTTP 204 No Content on successImportar requisiciones
/api/v1/requisitionsAPI Key requeridaImportar una o más requisiciones de compra desde tu ERP. Soporta importación por lotes de hasta 100 requisiciones por solicitud. Usa external_id para deduplicación.
Cuerpo de la solicitud
El cuerpo debe contener un array requisitions con 1 a 100 objetos de requisición.
Objeto Requisición
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| external_id | string | requerido | ID único en tu ERP (clave de deduplicación). Máx 250 caracteres. |
| items | array | requerido | Items de línea (1-200 items). Ver esquema de Item abajo. |
| requester_name | string | opcional | Nombre de la persona solicitante. Máx 200 caracteres. |
| requester_email | string | opcional | Email del solicitante. Máx 200 caracteres. |
| assigned_buyer | string | opcional | Comprador asignado en el ERP de origen (texto libre). Se muestra y filtra en el triage. Máx 200 caracteres. |
| description | string | opcional | Título o resumen de la requisición. Máx 500 caracteres. |
| comments | string | opcional | Instrucciones adicionales para compradores (HTML soportado). Máx 15.000 caracteres. |
| delivery_address | string | opcional | Dirección de entrega en texto libre. Máx 500 caracteres. |
| delivery_address_code | string | opcional | Código que coincide con una dirección configurada en Sourced. Máx 50 caracteres. |
| desired_delivery_lead_time_days | integer | opcional | Plazo de entrega deseado en días desde la confirmación de la OC. Se aplica como default a todos los items. |
| created_date | datetime | opcional | Fecha de creación de la requisición en tu sistema (ISO 8601). Se muestra como "Creación" en el triage. No es una fecha de entrega: la necesidad va por item en desired_delivery_date. |
| offer_deadline | datetime | opcional | Fecha límite para cotizaciones de proveedores (ISO 8601). |
| total_estimated_value | float | opcional | Valor total estimado. Se calcula automáticamente desde los items si se omite. |
| currency | string | opcional | Código de moneda (ej: "ARS", "USD"). Por defecto "ARS". Máx 10 caracteres. |
| department_code | string | opcional | Código de departamento/centro de costo (se compara con departamentos de Sourced). Máx 50 caracteres. |
| priority_level | string | opcional | Prioridad: "LOW", "MEDIUM", "HIGH" o "CRITICAL". |
| attachments | array | opcional | Archivos adjuntos (máx 20). Ver esquema de Adjunto abajo. |
| raw_data | object | opcional | JSON arbitrario de tu ERP, almacenado para trazabilidad. |
Objeto Item
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| description | string | requerido | Visible para el proveedorNombre o descripción del item. Máx 1.000 caracteres. |
| quantity | float | requerido | Visible para el proveedorCantidad requerida (debe ser > 0). |
| external_line_id | string | opcional | ID de línea en tu ERP (ej: "REQ-001-L10"). Se devuelve en las OC para trazabilidad. Máx 100 caracteres. |
| unit_of_measure | string | opcional | Visible para el proveedorCódigo de UOM (ej: "KG", "EA", "LT", "M", "UN"). Se compara con el catálogo de tu org. Máx 50 caracteres. |
| target_price | float | opcional | Precio objetivo/presupuesto por unidad. |
| estimated_price | float | opcional | Precio total estimado para esta línea. |
| currency | string | opcional | Moneda para precios (ej: "ARS", "USD"). Máx 10 caracteres. |
| category | string | opcional | Categoría desde tu ERP. Máx 200 caracteres. |
| material_code | string | opcional | Visible para el proveedorCódigo de material/parte en tu ERP (ej: código de material SAP). Máx 100 caracteres. |
| specifications | object | opcional | Especificaciones técnicas. Ver esquema de Especificaciones abajo. |
| desired_delivery_date | datetime | opcional | Fecha de entrega deseada para este item (ISO 8601, ej: "2026-05-15T00:00:00Z"). |
| desired_delivery_lead_time_days | integer | opcional | Plazo de entrega deseado en días para esta línea. Sobreescribe el valor a nivel de cabecera. |
| detail | string | opcional | Visible para el proveedorDato técnico o nota de esta línea. Máx 1.000 caracteres. Se comparte con el proveedor: se incluye en el mail de solicitud de cotización bajo «Especificaciones», salvo que mandes specifications.technical_specs, que tiene prioridad. No lo uses para notas internas de compras. |
| raw_data | object | opcional | JSON arbitrario para este item. |
Objeto Especificaciones
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| manufacturer_code | string | opcional | Visible para el proveedorNúmero de parte del fabricante (ej: "6ES7214-1AG40-0XB0"). Máx 100 caracteres. |
| manufacturer_name | string | opcional | Visible para el proveedorNombre del fabricante (ej: "Siemens"). Máx 200 caracteres. |
| manufacturer_description | string | opcional | Descripción del fabricante. Máx 500 caracteres. |
| buyer_code | string | opcional | Visible para el proveedorCódigo interno en tu sistema (ej: "MAT-001234"). Máx 100 caracteres. |
| buyer_code_description | string | opcional | Descripción del código interno. Máx 500 caracteres. |
| buyer_code_system | string | opcional | Nombre del sistema de origen (ej: "SAP", "Calipso"). Máx 50 caracteres. |
| technical_specs | string | opcional | Visible para el proveedorEspecificaciones técnicas en texto libre (ej: "220V, 50Hz, IP55"). Máx 2.000 caracteres. Se comparte con el proveedor en el mail de solicitud de cotización. Tiene prioridad sobre el detail del ítem. |
| requirements | string | opcional | Requisitos adicionales (ej: "Certificación ISO 9001 requerida"). Máx 2.000 caracteres. Uso interno: no se incluye en el mail al proveedor. |
Objeto Adjunto
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| url | string | requerido | URL públicamente accesible para descargar el archivo. Máx 2.000 caracteres. |
| filename | string | requerido | Nombre de archivo original (ej: "plano_motor.pdf"). Máx 255 caracteres. |
| description | string | opcional | Descripción del adjunto. Máx 500 caracteres. |
| file_type | string | opcional | Tipo MIME (se detecta automáticamente si no se proporciona). Máx 100 caracteres. |
Lógica de deduplicación
Cada requisición se identifica por su external_id (dentro del alcance de tu organización):
- Nueva: Si no se encuentra una requisición existente, se crea una nueva con estado
PENDING. - Actualizar: Si se encuentra una requisición existente con estado
PENDING, se actualiza in-place (los items se reemplazan completamente). - Superseder: Si se encuentra una requisición existente con estado
SUPERSEDED, se reactiva (vuelve a PENDING) con los nuevos datos. - Lanzada: Si la requisición ya fue lanzada como Solicitud de Compra (
LAUNCHED), la importación se rechaza. La PR activa no puede ser sobreescrita. - Descartada: Si la requisición fue descartada previamente (
DISCARDED), se reactiva (vuelve a PENDING) con los nuevos datos. Útil cuando el ERP la cancela y luego la reenvía.
curl -X POST https://api.gosourced.ai/api/v1/requisitions \
-H "X-API-Key: sk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"requisitions": [
{
"external_id": "REQ-2026-0042",
"items": [
{
"description": "Bearing SKF 6205",
"quantity": 10
}
]
}
]
}'Respuesta
ERPImportResult
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| success | boolean | requerido | true si error_count es 0. |
| created_count | integer | requerido | Cantidad de nuevas requisiciones creadas. |
| updated_count | integer | requerido | Cantidad de requisiciones PENDING existentes actualizadas. |
| skipped_count | integer | requerido | Cantidad de requisiciones omitidas (actualmente siempre 0). |
| error_count | integer | requerido | Cantidad de requisiciones que fallaron al importar. |
| errors | array | requerido | Array de {index, external_id, error} para cada requisición fallida. |
| requisition_ids | array | requerido | IDs internos de Sourced de las requisiciones creadas/actualizadas. |
{
"success": true,
"created_count": 1,
"updated_count": 0,
"skipped_count": 0,
"error_count": 0,
"errors": [],
"requisition_ids": [1234]
}Listar requisiciones
/api/v1/requisitionsAPI Key requeridaObtener una lista paginada de tus requisiciones importadas. Solo devuelve requisiciones creadas a través de la API.
Parámetros de consulta
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| page | integer | opcional | Número de página (por defecto: 1). |
| page_size | integer | opcional | Items por página, 1-100 (por defecto: 20). |
| status | string | opcional | Filtrar por estado: "PENDING", "LAUNCHED", "DISCARDED", "SUPERSEDED". |
curl -H "X-API-Key: sk_live_abc123..." \
"https://api.gosourced.ai/api/v1/requisitions?page=1&page_size=20&status=PENDING"{
"data": [
{
"id": 1234,
"external_id": "REQ-2026-0042",
"status": "PENDING",
"requester_name": "Juan Pérez",
"description": "Repuestos línea producción",
"items": [...],
"imported_at": "2026-03-07T14:30:00Z"
}
],
"total": 45,
"page": 1,
"page_size": 20,
"has_more": true
}Detalle de requisición
/api/v1/requisitions/{requisition_id}API Key requeridaObtener una requisición individual por su ID interno de Sourced, incluyendo todos los items.
curl -H "X-API-Key: sk_live_abc123..." \
https://api.gosourced.ai/api/v1/requisitions/1234Devuelve el objeto completo de la requisición. Devuelve 404 si no se encuentra o no pertenece a tu organización.
Verificar requisiciones existentes
/api/v1/requisitions/check-existingAPI Key requeridaVerificar qué external_ids ya existen en Sourced antes de importar. Útil para evitar llamadas innecesarias a la API.
Cuerpo de la solicitud
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| external_ids | array | requerido | Lista de strings external_id a verificar (1-100 items). |
curl -X POST https://api.gosourced.ai/api/v1/requisitions/check-existing \
-H "X-API-Key: sk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"external_ids": ["REQ-2026-0042", "REQ-2026-0043", "REQ-2026-0044"]
}'{
"existing": ["REQ-2026-0042"],
"not_found": ["REQ-2026-0043", "REQ-2026-0044"]
}Cancelar requisición
/api/v1/requisitions/cancelAPI Key requeridaCancelar una requisición PENDING enviando el external_id en el cuerpo del request.
Solo se pueden cancelar requisiciones con estado PENDING. Si la requisición ya fue lanzada como Solicitud de Compra, debe cancelarse dentro de Sourced.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| external_id | string | requerido | ID de la requisición en tu sistema externo (la misma que usaste al importar) |
| reason | string | opcional | Motivo de la cancelación (se almacena para auditoría) |
curl -X POST https://api.gosourced.ai/api/v1/requisitions/cancel \
-H "X-API-Key: sk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"external_id": "REQ-2026-0042",
"reason": "Cancelado desde ERP"
}'{
"status": "discarded",
"external_id": "REQ-2026-0042"
}Respuestas posibles:
discarded- La requisición fue descartada exitosamente.already_discarded- La requisición ya estaba descartada (idempotente).already_superseded- La requisición ya fue reemplazada por una versión más nueva.- La requisición ya fue lanzada - devuelve
409con detalles. - Requisición no encontrada - devuelve
404.
Listar órdenes de compra
/api/v1/purchase-ordersAPI Key requeridaObtener órdenes de compra de tu organización. Soporta sincronización incremental a través del parámetro 'since'.
Parámetros de consulta
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| page | integer | opcional | Número de página (por defecto: 1). |
| page_size | integer | opcional | Items por página, 1-100 (por defecto: 20). |
| status | string | opcional | Filtrar por estado: "DRAFT", "CREATED", "SENT", "CONFIRMED", "REJECTED", "CANCELLED". |
| since | datetime | opcional | Timestamp ISO 8601. Devuelve solo OC creadas o actualizadas después de esta fecha. |
| requisition_external_id | string | opcional | Filtrar por el external_id de la requisición original en tu ERP. Devuelve las OC que se generaron a partir de esa requisición. |
curl -H "X-API-Key: sk_live_abc123..." \
"https://api.gosourced.ai/api/v1/purchase-orders?since=2026-03-01T00:00:00Z&status=CONFIRMED"{
"data": [
{
"id": 567,
"code": "PO-2026-0089",
"status": "CONFIRMED",
"award_type": "FULL",
"supplier_name": "Distribuidora Industrial SA",
"supplier_id": 42,
"supplier_external_id": "PROV-CAL-00123",
"supplier_tax_id": "30712345678",
"supplier_email": "ventas@distribuidora.com",
"total_price": 285000.00,
"currency": "ARS",
"delivery_address": "Av. Corrientes 1234, CABA",
"delivery_address_code": "001",
"observations": "Entregar en horario de mañana. Coordinar con depósito.",
"delivery_lead_time_days": 15,
"expected_delivery_date": "2026-03-20T16:00:00Z",
"payment_terms_code": "NET30",
"awarded_at": "2026-03-05T16:00:00Z",
"awarded_by_name": "Juan Pérez",
"awarded_by_email": "juan.perez@empresa.com",
"created_at": "2026-03-05T16:00:00Z",
"updated_at": "2026-03-06T10:30:00Z",
"purchase_request_id": 1234,
"requisition_external_id": "REQ-2026-00142",
"exchange_rate_data": {
"date": "2026-03-05",
"usdToArs": 1450.0,
"arsToUsd": 0.00069
},
"total_nominal_savings": 15000.00,
"total_real_savings": 8500.00,
"savings_currency": "ARS",
"items": [
{
"description": "Bearing SKF 6205",
"quantity": 10,
"unit_price": 28500.00,
"total_price": 285000.00,
"unit_of_measure": "UN",
"currency": "ARS",
"delivery_lead_time_days": 15,
"expected_delivery_date": "2026-03-20T16:00:00Z",
"external_line_id": "REQ-2026-0042-L10",
"buyer_code": "MAT-001234",
"buyer_code_system": "SAP",
"manufacturer_code": "6205-2RS",
"technical_specs": "220V, 50Hz, IP55"
}
]
}
],
"total": 12,
"page": 1,
"page_size": 20,
"has_more": false
}Detalle de orden de compra
/api/v1/purchase-orders/{po_id}API Key requeridaObtener una orden de compra individual por su ID interno de Sourced, incluyendo todos los items con trazabilidad hacia tu ERP via external_line_id.
curl -H "X-API-Key: sk_live_abc123..." \
https://api.gosourced.ai/api/v1/purchase-orders/567Este endpoint requiere el ID interno de Sourced (po_id). Si necesitás buscar órdenes de compra usando el ID de requisición de tu ERP, usá el endpoint GET /purchase-orders con el query parameter requisition_external_id:
También podés combinarlo con otros filtros: status para filtrar por estado de la OC, since para sincronización incremental, y page / page_size para paginación.
Objeto Orden de Compra
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| id | integer | requerido | ID interno de OC en Sourced. |
| code | string | requerido | Código de OC legible. |
| status | string | requerido | Estado de la OC: "DRAFT", "CREATED", "SENT", "CONFIRMED", "REJECTED", "CANCELLED". |
| award_type | string | opcional | Tipo de adjudicación: FULL (proveedor único) o PARTIAL (adjudicación dividida entre múltiples proveedores). |
| supplier_name | string | requerido | Nombre del proveedor. |
| supplier_id | integer | opcional | ID interno del proveedor en Sourced. |
| total_price | float | opcional | Valor total de la OC. |
| currency | string | opcional | Código de moneda. |
| delivery_address | string | opcional | Dirección de entrega. |
| delivery_address_code | string | opcional | Código de la dirección de entrega de la organización vinculada (ej. "001"). Usalo para mapear el destino contra tu ERP. Null para direcciones de texto libre sin registro vinculado. |
| observations | string | opcional | Comentarios u observaciones en texto libre que el comprador cargó en el paso de revisión pre-adjudicación. Usa las condiciones especiales cotizadas por el proveedor si el comprador no dejó nota. Null cuando no hay ninguna. |
| delivery_lead_time_days | integer | opcional | Plazo de entrega en días, según lo cotizado por el proveedor a nivel cabecera. Cuando el proveedor cotiza distintos plazos por línea, los items pueden tener valores diferentes - usá items[].delivery_lead_time_days para el valor por línea. |
| expected_delivery_date | datetime | opcional | Fecha de entrega calculada (awarded_at + delivery_lead_time_days). Null si falta cualquiera de los dos. Para fechas por línea usá items[].expected_delivery_date. |
| payment_terms_code | string | opcional | Código de condiciones de pago. |
| awarded_at | datetime | opcional | Cuándo se adjudicó la OC (ISO 8601). |
| awarded_by_name | string | opcional | Nombre del usuario que adjudicó la OC. |
| awarded_by_email | string | opcional | Email del usuario que adjudicó la OC. |
| created_at | datetime | opcional | Timestamp de creación (ISO 8601). |
| updated_at | datetime | opcional | Timestamp de última actualización (ISO 8601). |
| purchase_request_id | integer | opcional | ID de la solicitud de compra originante. |
| requisition_external_id | string | opcional | ID del documento origen en tu ERP (ej. número de requisición en Calipso/SAP/JDE). Se resuelve desde la ERPRequisition de origen cuando existe; si no, cae al external_id de la PR. Puede ser una lista separada por comas cuando una sola PR agrupa varias requisiciones origen. |
| exchange_rate_data | object | opcional | Tipos de cambio al momento del award. Formato: {"date": "2026-03-05", "usdToArs": 1450.0, "arsToUsd": 0.00069}. Null si no hay datos disponibles. |
| total_nominal_savings | float | opcional | Ahorro nominal total vs precios históricos (sin ajuste por inflación). |
| total_real_savings | float | opcional | Ahorro real total vs precios históricos (ajustado por inflación). |
| savings_currency | string | opcional | Moneda de los montos de ahorro. |
| items | array | requerido | Items de línea de la OC. Ver esquema de Item de OC abajo. |
Objeto Item de OC
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| description | string | requerido | Descripción del item. |
| quantity | float | opcional | Cantidad ordenada. |
| unit_price | float | opcional | Precio por unidad. |
| total_price | float | opcional | Precio total de la línea (cantidad x precio unitario). |
| unit_of_measure | string | opcional | Unidad de medida. |
| currency | string | opcional | Código de moneda. |
| delivery_lead_time_days | integer | opcional | Plazo de entrega en días para esta línea, según lo cotizado por el proveedor. Items distintos de una misma OC pueden tener plazos diferentes. |
| expected_delivery_date | datetime | opcional | Fecha de entrega calculada para esta línea (PO.awarded_at + delivery_lead_time_days del item). Null si falta cualquiera de los dos. |
| external_line_id | string | opcional | ID de línea original de tu ERP - usalo para vincular items de la OC con tus líneas de requisición. |
| buyer_code | string | opcional | Código interno en tu sistema, tal como lo enviaste al importar la requisición (ej: "MAT-001234"). |
| buyer_code_system | string | opcional | Sistema de origen del código interno (ej: "SAP", "JDE", "Calipso"). |
| buyer_code_description | string | opcional | Descripción del código interno. |
| manufacturer_code | string | opcional | Número de parte del fabricante. |
| manufacturer_name | string | opcional | Nombre del fabricante (ej: "Siemens"). |
| manufacturer_description | string | opcional | Descripción del fabricante para la parte. |
| technical_specs | string | opcional | Especificaciones técnicas en texto libre (ej: "220V, 50Hz, IP55"). |
| requirements | string | opcional | Requisitos adicionales (ej: "Certificación ISO 9001 requerida"). |
- El plazo de entrega existe a dos niveles:
delivery_lead_time_daysa nivel cabecera (refleja el plazo general que cotizó el proveedor) yitems[].delivery_lead_time_daysa nivel línea (preciso por item). Cuando el proveedor cotiza distintos plazos por línea, conviene usar el valor por línea. expected_delivery_datees un campo calculado:awarded_at + delivery_lead_time_days. Lo computa Sourced; no es un dato que el proveedor envíe directamente.- Si
delivery_lead_time_daysviene null, también lo haráexpected_delivery_date. Esto suele significar que el proveedor no incluyó el plazo en su cotización.
Descargar legajo de la OC (expediente)
/api/v1/purchase-orders/{po_id}/legajoAPI Key requeridaObtiene un link temporal para descargar el legajo - un ZIP con la traza completa de la adjudicación: PDF/XLSX de comparativa, historial de emails y los adjuntos de todos los proveedores participantes.
curl -H "X-API-Key: sk_live_abc123..." \
https://api.gosourced.ai/api/v1/purchase-orders/567/legajoObjeto de respuesta
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| download_url | string | requerido | URL presignada temporal para descargar el ZIP del legajo. |
| filename | string | requerido | Nombre sugerido para el archivo ZIP. |
| expires_in | integer | requerido | Segundos hasta que la URL de descarga expira. |
| size_bytes | integer | requerido | Tamaño del ZIP del legajo en bytes. |
- La respuesta es una URL presignada temporal (válida ~10 minutos). Descarga el ZIP directamente desde ahí - esa URL no requiere API key.
- El legajo corresponde a la solicitud (PR) detrás de la OC. En una adjudicación dividida (varias OC de una misma solicitud), las OC hermanas devuelven el mismo expediente.
Comparativa de cotizaciones de la OC
/api/v1/purchase-orders/{po_id}/quotationsAPI Key requeridaObtiene la comparativa completa de cotizaciones que originó esta adjudicación: todas las líneas cotizadas por cada proveedor para la solicitud (PR) detrás de la OC, adjudicadas y no adjudicadas - no solo las que terminaron en esta OC.
curl -H "X-API-Key: sk_live_abc123..." \
https://api.gosourced.ai/api/v1/purchase-orders/567/quotations{
"purchase_order_id": 567,
"purchase_order_code": "PO-4F2A91C3",
"requisition_external_id": "REQ-2026-0042",
"exchange_rate": {
"date": "2026-07-12",
"base": "USD",
"rates": { "ARS": 1450.5, "EUR": 0.92 }
},
"currencies": ["ARS", "USD"],
"items": [
{
"material_code": "MAT-000123",
"external_line_id": "10",
"description": "Guantes de nitrilo talle L",
"quantity": 500.0,
"quotes": [
{
"quotation_id": 8123,
"supplier_id": 42,
"supplier_name": "Proveedor A S.A.",
"supplier_tax_id": "30516242775",
"supplier_erp_code": "PROV-001",
"unit_price": 11.25,
"original_price": 12.5,
"discount": { "type": "percentage", "value": 10, "source": "manual" },
"currency": "ARS",
"quantity": 500,
"awarded": true,
"awarded_po_code": "PO-4F2A91C3",
"lead_time_days": 7,
"payment_term_code": "NET30",
"response_status": "PARTIAL",
"received_at": "2026-07-10T14:32:00+00:00"
},
{
"quotation_id": 8124,
"supplier_id": 57,
"supplier_name": "Proveedor B S.R.L.",
"supplier_tax_id": "30709876541",
"supplier_erp_code": null,
"unit_price": 0.0095,
"original_price": 0.0095,
"discount": null,
"currency": "USD",
"quantity": 500,
"awarded": false,
"awarded_po_code": null,
"lead_time_days": 15,
"payment_term_code": null,
"response_status": "INCOMPLETE",
"received_at": "2026-07-11T09:05:00+00:00"
}
]
}
]
}Objeto de respuesta
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| purchase_order_id | integer | requerido | ID de la OC consultada. |
| purchase_order_code | string | requerido | Código de la OC consultada (p. ej. PO-4F2A91C3). |
| requisition_external_id | string | opcional | ID externo de la requisición de origen (sistema ERP), si existe. |
| exchange_rate | object | opcional | Tipos de cambio guardados en la OC al momento de la adjudicación, para convertir las cotizaciones a una moneda común: { date, base: "USD", rates: { ARS: 1450.5, EUR: 0.92 } } (unidades de cada moneda por 1 USD). null si la OC no guardó una foto (licitación, catálogo u OC anterior a esta función). |
| currencies | array | requerido | Monedas distintas que aparecen en las cotizaciones, ordenadas (p. ej. ["ARS", "USD"]). |
| items | array | requerido | Líneas de la solicitud detrás de la OC, cada una con sus cotizaciones. |
Objeto ítem (items[])
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| material_code | string | opcional | Código de material del comprador, si la línea tiene uno. |
| external_line_id | string | opcional | ID de línea del sistema de origen (ERP), si existe. |
| description | string | requerido | Descripción del ítem solicitado. |
| quantity | float | opcional | Cantidad solicitada. |
| quotes | array | requerido | Cotizaciones recibidas para esta línea, una por proveedor que cotizó con precio. |
Objeto cotización (items[].quotes[])
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| quotation_id | integer | requerido | ID de la cotización. Un proveedor que cotizó más de una vez (rondas) aparece una vez por cotización. |
| supplier_id | integer | opcional | ID interno del proveedor en Sourced. Identidad estable entre líneas (el nombre puede repetirse). |
| supplier_name | string | opcional | Nombre del proveedor que cotizó. |
| supplier_tax_id | string | opcional | Identificador fiscal del proveedor (CUIT en Argentina, CNPJ en Brasil, RUT en Chile/Uruguay). |
| supplier_erp_code | string | opcional | Código ERP del proveedor en tu organización, si está configurado. |
| unit_price | float | opcional | Precio unitario FINAL, neto de descuento, en la moneda original del proveedor. Es el mismo número que ve el comprador en la comparativa de adjudicación y en el Excel del legajo, y el precio al que se adjudicó la OC. |
| original_price | float | opcional | Precio unitario antes del descuento. Igual a unit_price si no hubo descuento. |
| discount | object | opcional | Descuento aplicado: { type: "percentage" | "fixed", value, source }. source: supplier_quoted (lo ofreció el proveedor), negotiation, manual (lo cargó el comprador) o global. null si no hubo descuento. |
| currency | string | opcional | Moneda de la cotización (código ISO, p. ej. ARS, USD). Si el proveedor no indicó moneda en ninguna parte, se informa "USD". |
| quantity | float | opcional | Cantidad cotizada por el proveedor. |
| awarded | boolean | requerido | true si esta línea se adjudicó a este proveedor en alguna OC activa de la solicitud. |
| awarded_po_code | string | opcional | Código de la OC donde se adjudicó la línea (puede ser una OC hermana en adjudicación dividida). null si no fue adjudicada. |
| lead_time_days | integer | opcional | Plazo de entrega ofrecido, en días (entero). Un plazo no numérico se informa como null. |
| payment_term_code | string | opcional | Código de condición de pago ofrecida por el proveedor. |
| response_status | string | opcional | Clasificación del análisis AI de la respuesta del proveedor. Valores como INCOMPLETE, PARTIAL, NEEDS_HUMAN_REVIEW, EXPLICIT_REJECTION. |
| received_at | datetime | opcional | Fecha y hora de recepción de la cotización (ISO 8601). |
- La comparativa corresponde a la solicitud (PR) detrás de la OC: incluye las cotizaciones de todos los proveedores que participaron, aunque no hayan ganado. Una línea sin precio cotizado no genera entrada en quotes; una OC de catálogo (sin proceso de cotización) devuelve quotes vacíos.
awardedes a nivel solicitud: en una adjudicación dividida, una línea puede haberse adjudicado en una OC hermana distinta de la consultada -awarded_po_codesiempre indica el código de la OC ganadora de esa línea.- Los precios son los mismos que el comprador vio en la comparativa de adjudicación y los que figuran en el Excel del legajo:
unit_priceya tiene aplicado cualquier descuento negociado (original_priceydiscountmuestran el bruto y el descuento). Una cotización importada que el usuario todavía no confirmó no aparece. - Los precios se devuelven en la moneda cotizada por cada proveedor - no se aplica conversión.
exchange_ratetrae los tipos de cambio guardados en la OC al adjudicar (null si no hay) para que puedas convertir todo a una moneda común. - Todas las claves están siempre presentes; si un dato falta, el valor es null (el shape de la respuesta nunca cambia).
Actualizar estado de orden de compra
/api/v1/purchase-orders/{po_id}/statusAPI Key requeridaActualizar el estado de una orden de compra para reflejar su progreso en tu sistema externo (ERP).
Transiciones de estado permitidas:
| Desde | Hacia | Significado |
|---|---|---|
| DRAFT | CREATED | La OC fue creada en tu ERP. |
| CREATED | CONFIRMED | La OC fue aprobada completamente en tu ERP. |
| DRAFT | CONFIRMED | Atajo cuando no necesitás el paso intermedio. |
| DRAFT | REJECTED | La OC fue rechazada en tu ERP. La PR se reabre en Sourced. |
| CREATED | REJECTED | La OC fue rechazada en tu ERP después de haber sido creada. La PR se reabre en Sourced. |
| DRAFT / CREATED / SENT / CONFIRMED | CANCELLED | La compra se cayó. La OC se cancela y la PR también - no se reabre nada. Usá REJECTED si la necesidad sigue vigente y querés re-adjudicar. |
REJECTED y CANCELLED funcionan también cuando la OC fue una adjudicación parcial, siempre que sea la única OC activa de su PR. Si la PR tiene varias OCs activas (split a varios proveedores), el endpoint devuelve 409 y la reversión debe gestionarse dentro de Sourced.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| status | string | requerido | Estado destino: CREATED (OC creada en tu ERP), CONFIRMED (OC aprobada en tu ERP), REJECTED (OC rechazada, reabre la PR) o CANCELLED (compra caída, cancela la OC y la PR). |
| external_id | string | opcional | Número o código de la OC en tu ERP (ej: OC-CAL-00045678). Se guarda para trazabilidad. |
| notes | string | opcional | Notas opcionales sobre el cambio de estado. |
curl -X POST https://api.gosourced.ai/api/v1/purchase-orders/567/status \
-H "X-API-Key: sk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"status": "CREATED",
"external_id": "OC-CAL-00045678",
"notes": "Creada en Calipso"
}'{
"status": "created",
"po_id": 567,
"code": "PO-A1B2C3D4"
}Editar una OC
/api/v1/purchase-orders/{po_id}API Key requeridaActualiza los campos editables de una orden de compra. Solo se tocan los campos presentes en el body: número de OC en tu ERP, condición de pago, plazo de entrega, observaciones y estado (misma máquina de transiciones que POST /status).
Cuerpo de la solicitud
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| external_id | string | opcional | Número o código de la OC en tu ERP. Sobrescribe el valor guardado. |
| payment_terms_code | string | opcional | Código de condición de pago del catálogo (p. ej. NET30). Insensible a mayúsculas; se valida contra los códigos activos - un código desconocido devuelve 400 con la lista permitida. |
| delivery_lead_time_days | integer | opcional | Plazo de entrega en días desde la confirmación de la OC. expected_delivery_date se deriva de este valor. |
| observations | string | opcional | Texto libre que se muestra en la OC. Una cadena vacía la limpia. |
| status | string | opcional | Estado destino: CREATED, CONFIRMED, REJECTED o CANCELLED - mismas transiciones que POST /status. Enviar el estado actual es un no-op. REJECTED/CANCELLED devuelven el contrato de esos flujos (pr_reopened / pr_code) más updated_fields. |
| notes | string | opcional | Notas sobre el cambio de estado. Solo válido junto con status. |
curl -X PATCH https://api.gosourced.ai/api/v1/purchase-orders/567 \
-H "X-API-Key: sk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"external_id": "OC-CAL-00045678",
"payment_terms_code": "NET30",
"status": "CONFIRMED"
}'{
"status": "updated",
"po_id": 567,
"code": "PO-A1B2C3D4",
"updated_fields": ["external_id", "payment_terms_code", "status"]
}- Partial update real: solo se aplican los campos presentes en el body. Enviar null explícito es un error (400) - para dejar un campo como está, se omite.
- Toda la validación corre antes de aplicar nada: si la respuesta es 400 o 409, ningún campo del body se modificó.
- El cambio de estado usa la misma máquina de transiciones que
POST /purchase-orders/{po_id}/status(que sigue disponible). Una transición inválida devuelve 409. Editar una OC CANCELLED o REJECTED devuelve 409. - Cambiar la condición de pago o el plazo de entrega no regenera el PDF que ya recibió el proveedor: el dato se actualiza en Sourced, pero el documento enviado no cambia.
- El contenido de la adjudicación (precios, ítems, moneda, proveedor) no es editable: para corregirlo, rechaza la OC (status REJECTED) y vuelve a adjudicar.
Listar proveedores
/api/v1/suppliersAPI Key requeridaListar los proveedores de tu organización. Usa ?has_erp_code=false para encontrar proveedores sin mapear al ERP. Usa ?detail=full para obtener contactos, categorías y cobertura.
Parámetros de consulta
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| page | integer | opcional | Número de página (default: 1). |
| page_size | integer | opcional | Resultados por página (1-200, default: 50). |
| search | string | opcional | Buscar por nombre, nombre personalizado, email, CUIT/CNPJ o código ERP. |
| erp_code | string | opcional | Filtrar por código ERP exacto. |
| has_erp_code | boolean | opcional | true = solo mapeados al ERP, false = solo sin mapear. |
| detail | string | opcional | Usar 'full' para incluir contactos, categorías y cobertura. |
# Light (default)
curl -H "X-API-Key: sk_live_abc123..." \
"https://api.gosourced.ai/api/v1/suppliers?has_erp_code=false"
# Full detail
curl -H "X-API-Key: sk_live_abc123..." \
"https://api.gosourced.ai/api/v1/suppliers?search=30712345678&detail=full"// Light response
{
"data": [
{
"id": 42,
"name": "Distribuidora Industrial SA",
"email": "ventas@distribuidora.com",
"tax_id": "30712345678",
"country_code": "AR",
"city": "Buenos Aires",
"is_active": true,
"is_preferred": true,
"erp_code": "PROV-CAL-00123",
"erp_type": "SAP_B1"
}
],
"total": 85,
"page": 1,
"page_size": 50,
"has_more": true
}
// Full response (?detail=full)
{
"data": [
{
"id": 42,
"name": "Distribuidora Industrial SA",
"email": "ventas@distribuidora.com",
"tax_id": "30712345678",
"country_code": "AR",
"city": "Buenos Aires",
"is_active": true,
"is_preferred": true,
"erp_code": "PROV-CAL-00123",
"erp_type": "SAP_B1",
"address": "Av. Corrientes 1234",
"state_code": "CABA",
"website": "https://distribuidora.com",
"description": "Distribuidor de insumos industriales",
"tax_regime": null,
"employee_count": "50+",
"years_in_business": "5+",
"performance_score": 4.2,
"reliability_score": 4.5,
"quality_score": 4.0,
"contacts": [
{
"name": "Juan Pérez",
"email": "juan@distribuidora.com",
"phone": "+54 11 5555-1234",
"role": "SALES",
"is_primary": true
}
],
"categories": [
{ "id": 15, "code": "IND-001", "name": "Insumos Industriales", "level": "LEVEL_1" }
],
"coverage": [
{ "country": "Argentina", "province": null, "is_nationwide": true }
]
}
],
"total": 1,
"page": 1,
"page_size": 50,
"has_more": false
}Crear proveedor
/api/v1/suppliersAPI Key requeridaCrear un proveedor para tu organización. Al menos uno de tax_id o erp_code es obligatorio. Se requiere al menos un contacto con rol 'sales'. Si ya existe un proveedor global con el mismo CUIT, se reutiliza y se vincula a tu organización.
Cuerpo de la solicitud
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| name | string | requerido | Nombre del proveedor para tu organización. |
| tax_id | string | opcional | CUIT, CNPJ, RUT, etc. Al menos uno de tax_id o erp_code es obligatorio. |
| erp_code | string | opcional | Código del proveedor en tu ERP. Al menos uno de tax_id o erp_code es obligatorio. |
| erp_type | string | opcional | Tipo de ERP (SAP_B1, JDE, ORACLE_CLOUD, etc.). |
| country_code | string | opcional | Código de país ISO 3166-1 (ej: AR, BR, US). |
| city | string | opcional | Ciudad del proveedor. |
| address | string | opcional | Dirección completa. |
| state_code | string | opcional | Estado/provincia (ej: CABA, SP). |
| website | string | opcional | Sitio web del proveedor. |
| contacts | array | requerido | Lista de contactos. Al menos uno con rol 'sales' es obligatorio. |
| contacts[].name | string | requerido | Nombre del contacto. |
| contacts[].email | string | requerido | Email del contacto. |
| contacts[].phone | string | opcional | Teléfono (opcional). |
| contacts[].role | string | requerido | Rol: SALES o LOGISTICS. |
| contacts[].is_primary | boolean | opcional | true si es el contacto principal (default: false). |
curl -X POST https://api.gosourced.ai/api/v1/suppliers \
-H "X-API-Key: sk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"name": "Distribuidora Industrial SA",
"tax_id": "30712345678",
"erp_code": "PROV-CAL-00123",
"erp_type": "SAP_B1",
"country_code": "AR",
"city": "Buenos Aires",
"address": "Av. Corrientes 1234",
"contacts": [
{
"name": "Juan Pérez",
"email": "juan@distribuidora.com",
"phone": "+54 11 5555-1234",
"role": "SALES",
"is_primary": true
},
{
"name": "María García",
"email": "maria@distribuidora.com",
"role": "LOGISTICS"
}
]
}'// 201 Created
{
"id": 42,
"name": "Distribuidora Industrial SA",
"email": null,
"tax_id": "30712345678",
"country_code": "AR",
"city": "Buenos Aires",
"is_active": true,
"is_preferred": false,
"erp_code": "PROV-CAL-00123",
"erp_type": "SAP_B1",
"address": "Av. Corrientes 1234",
"state_code": null,
"website": null,
"description": null,
"tax_regime": null,
"employee_count": null,
"years_in_business": null,
"performance_score": null,
"reliability_score": null,
"quality_score": null,
"contacts": [
{
"name": "Juan Pérez",
"email": "juan@distribuidora.com",
"phone": "+54 11 5555-1234",
"role": "SALES",
"is_primary": true
},
{
"name": "María García",
"email": "maria@distribuidora.com",
"phone": null,
"role": "LOGISTICS",
"is_primary": false
}
],
"categories": [],
"coverage": []
}Crear o actualizar seguimientos de entrega
/api/v1/delivery-trackingsAPI Key requeridaUpsert por lotes de 1 a 200 líneas de OC para seguir su entrega. La clave natural es (po_code, line_position): reenviar la misma línea actualiza el seguimiento existente en vez de duplicarlo.
Límite de tasa: 30 solicitudes por minuto (los demás endpoints de este grupo usan el límite general de 60 por minuto).
Cuerpo de la solicitud
El cuerpo debe contener un array lines con 1 a 200 líneas. Cada línea lleva la OC, la posición, la fecha comprometida, el proveedor (código ERP o CUIT) y el detalle del ítem (descripción, cantidad pedida y cantidad pendiente): sin eso no hay nada que seguir ni qué mostrarle al proveedor. Un campo obligatorio ausente rechaza el batch completo con 422.
Objeto de línea (lines[])
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| po_code | string | requerido | Número de OC en el sistema del cliente. Máximo 100 caracteres. |
| line_position | integer | requerido | Posición de la línea dentro de la OC (10, 20, 30…). Entero ≥ 0. |
| original_delivery_date | date | requerido | Fecha de entrega comprometida (YYYY-MM-DD) |
| supplier_erp_code | string | Uno de los dos | Código del proveedor en el ERP del cliente. Identificador recomendado. Obligatorio si no se envía supplier_tax_id. Máximo 100 caracteres. |
| supplier_tax_id | string | Uno de los dos | CUIT/RUT del proveedor. Obligatorio si no se envía supplier_erp_code. Máximo 50 caracteres. |
| supplier_name | string | opcional | Solo texto para mostrar; nunca se usa para matchear al proveedor. Máximo 255 caracteres. |
| material_code | string | opcional | Código del material o ítem en el sistema del cliente. Máximo 100 caracteres. |
| description | string | requerido | Descripción del ítem. Es lo que el proveedor ve en el mail de seguimiento. Máximo 500 caracteres. |
| quantity_ordered | float | requerido | Cantidad pedida. ≥ 0. Es lo que el proveedor ve en el mail de seguimiento. |
| quantity_pending | float | requerido | Cantidad pendiente de entrega. ≥ 0. Es lo que el mail le reclama al proveedor: con entregas parciales, mandá lo que falta, no lo pedido. |
| unit_of_measure | string | opcional | Unidad de medida. Máximo 20 caracteres. |
| po_date | date | opcional | Fecha de la OC (YYYY-MM-DD). |
| buyer_email | string | opcional | CC de escalación y destinatario de los avisos de atraso. Si no se envía, la notificación in-app de atraso va al primer usuario con rol logistics de la organización (uno solo, no a todos). |
| is_urgent | boolean | opcional | Marca la línea como urgente. |
Hay que enviar al menos supplier_erp_code o supplier_tax_id. Para supplier_tax_id, un valor vacío o compuesto solo por separadores (por ejemplo "-" o " - ") cuenta como no enviado, y el match ignora guiones y espacios en ambos lados (30-71234567-8 matchea 30712345678). supplier_erp_code solo se recorta (trim): un "-" literal SÍ cuenta como enviado y se usa tal cual para buscar — si no matchea ningún proveedor, la línea queda en SUPPLIER_NOT_FOUND (no es un error).
Reglas del upsert
- Línea nueva: se crea en PENDING_SCHEDULE si el proveedor resolvió, o en SUPPLIER_NOT_FOUND si no. Nace activa (sin pausar).
- Línea existente, abierta, creada por esta misma API: se actualizan sus datos y cantidades. Un cambio de original_delivery_date o de las cantidades no reinicia el ciclo de seguimiento.
- Línea existente ya cerrada (DELIVERED o CANCELLED): se reactiva con un ciclo de seguimiento nuevo (vuelve a mandar mails de confirmación); el resultado es reactivated y cuenta dentro de updated_count.
- Corregir el proveedor de una línea existente: si llega con un supplier_erp_code o supplier_tax_id que resuelve a OTRO proveedor, el seguimiento se re-vincula a esa empresa y el ciclo arranca de nuevo (los mails previos eran con la empresa equivocada). Una línea que no resuelve ningún proveedor nunca desvincula el proveedor ya asignado.
- Sin cambios: el resultado es unchanged; no se escribe ningún evento ni se toca updated_at.
- Línea administrada por otra fuente (CSV o sincronización ERP): se rechaza con el error dlvTrackingManagedByOtherSource. Los seguimientos cargados por CSV o por la sincronización ERP no se pueden modificar por esta API.
- Cantidad pendiente 0: la línea ya se recibió entera. Sobre un seguimiento abierto se cierra como DELIVERED (evento delivered con reason quantity_pending_zero; la fecha real queda como el día del envío — si la conocés, usá mark_delivered con actual_delivery_date). Sobre uno ya cerrado es unchanged, no lo reactiva. Una línea NUEVA con pendiente 0 devuelve el error dlvNothingPending.
curl -X POST https://api.gosourced.ai/api/v1/delivery-trackings \
-H "X-API-Key: sk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"lines": [
{
"po_code": "OC-2026-0455",
"line_position": 10,
"original_delivery_date": "2026-10-15",
"supplier_erp_code": "PROV-CAL-00123",
"material_code": "MAT-001234",
"description": "Rodamiento SKF 6205",
"quantity_ordered": 50,
"quantity_pending": 50,
"unit_of_measure": "UN",
"buyer_email": "compras@empresa.com"
},
{
"po_code": "OC-2026-0455",
"line_position": 20,
"original_delivery_date": "2026-10-15",
"supplier_tax_id": "30-71234567-8",
"description": "Filtro de aceite",
"quantity_ordered": 12,
"quantity_pending": 12,
"unit_of_measure": "UN"
}
]
}'Respuesta
Respuesta del upsert
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| created_count | integer | requerido | Cantidad de líneas creadas. |
| updated_count | integer | requerido | Cantidad de líneas actualizadas (incluye las reactivadas). |
| unchanged_count | integer | requerido | Cantidad de líneas sin cambios. |
| error_count | integer | requerido | Cantidad de líneas con error. |
| results | array | requerido | Resultado de cada línea del batch, en el mismo orden en que se enviaron. |
Objeto de resultado por línea (results[])
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| po_code | string | requerido | Número de OC, tal como se envió. |
| line_position | integer | requerido | Posición de línea, tal como se envió. |
| id | integer | opcional | Id interno del seguimiento. Ausente cuando la línea terminó en error. |
| result | string | requerido | created | updated | unchanged | reactivated | error. |
| status | string | opcional | Estado del seguimiento después del upsert. Ausente en las líneas con error. |
| code | string | opcional | Código del error (dlv*). Solo presente cuando result es error. |
| message | string | opcional | Detalle del error en inglés, pensado para logs. No traducir ni mostrar al usuario final: el code es la clave i18n. |
// 200 OK
{
"created_count": 1,
"updated_count": 0,
"unchanged_count": 0,
"error_count": 1,
"results": [
{
"po_code": "OC-2026-0455",
"line_position": 10,
"id": 8821,
"result": "created",
"status": "PENDING_SCHEDULE"
},
{
"po_code": "OC-2026-0455",
"line_position": 20,
"result": "error",
"code": "dlvSupplierReferenceAmbiguous",
"message": "supplier_tax_id '30-71234567-8' matches more than one supplier"
}
]
}El HTTP siempre es 200, incluso si hay líneas con error: revisá error_count y el code de cada línea en results para saber qué falló.
Listar seguimientos de entrega
/api/v1/delivery-trackingsAPI Key requeridaObtener los seguimientos de entrega de tu organización, de todas las fuentes (API, CSV y sincronización ERP). Soporta sincronización incremental con since.
Parámetros de consulta
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| page | integer | opcional | Número de página (por defecto: 1). |
| page_size | integer | opcional | Items por página, 1-200 (por defecto: 50). |
| status | string | opcional | Filtrar por estado (ver la tabla de estados más abajo). Incluye STAND_BY. |
| po_code | string | opcional | Filtrar por el po_code exacto. |
| supplier_erp_code | string | opcional | Filtrar por el código ERP del proveedor. |
| since | datetime | opcional | Timestamp ISO 8601. Devuelve solo seguimientos creados o modificados desde esa fecha, en orden ascendente por fecha de modificación. Sin zona horaria se interpreta como UTC: mandá siempre un offset explícito (o "Z") para evitar ambigüedad. |
| open_only | boolean | opcional | Excluye los seguimientos DELIVERED y CANCELLED. |
since, el orden es por fecha de modificación ascendente (no por fecha de creación descendente, que es el orden por defecto): guardá el updated_at del último registro recibido y usalo como el próximo since. Para no perder filas escritas por una transacción larga (un CSV grande, un sync del ERP), el servidor corre el since 10 minutos hacia atrás: vas a volver a recibir registros de esos minutos, así que deduplicá por id (el upsert es idempotente de tu lado). Para un seguimiento que nunca se modificó, updated_at es igual a created_at (nunca null): el cursor siempre tiene un valor usable, sin que tengas que resolver vos el fallback.Estados
| Estado | Descripción |
|---|---|
| PENDING_SCHEDULE | Pendiente de programar. Cargada; todavía no se pidió confirmación al proveedor. |
| READY_TO_SEND | Lista para enviar. Lista para mandar el pedido de confirmación de entrega. |
| SENT_PENDING_RESPONSE | Enviada, esperando respuesta. Se pidió confirmación; el proveedor aún no respondió. |
| NO_RESPONSE | Sin respuesta. No respondió tras los recordatorios. |
| CONFIRMED_ON_TIME | Confirmada en fecha. El proveedor confirmó la fecha original. |
| CONFIRMED_DELAYED | Confirmada con demora. Confirmó, pero con fecha posterior a la original. |
| CONFIRMED_EARLY | Confirmada anticipada. Confirmó una fecha anterior a la original. |
| REQUIRES_REVIEW | Requiere revisión. Respondió algo ambiguo o cambió condiciones; hay que revisarlo. |
| DELIVERED | Entregada. Ya entregada. |
| CANCELLED | Cancelada. La OC fue cancelada. |
| SUPPLIER_NOT_FOUND | Proveedor no identificado. No se pudo matchear el proveedor (por código ERP o CUIT). |
| STAND_BY | Pausada manualmente y todavía abierta: no se le manda ningún mail. Es un estado virtual — un estado terminal (DELIVERED/CANCELLED) siempre le gana a STAND_BY, así que una línea pausada que se entrega o se cancela se informa por su estado real. |
curl -H "X-API-Key: sk_live_abc123..." \
"https://api.gosourced.ai/api/v1/delivery-trackings?since=2026-09-01T00:00:00Z&open_only=true"Objeto de seguimiento (delivery tracking)
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| id | integer | requerido | Id interno del seguimiento. |
| po_code | string | requerido | Número de OC en el sistema del cliente. |
| line_position | integer | requerido | Posición de la línea dentro de la OC. |
| source | string | requerido | Origen del seguimiento: api, csv o erp_sync. La API lee seguimientos de las tres fuentes. |
| supplier | object | opcional | Proveedor vinculado, o null si todavía no se pudo resolver (SUPPLIER_NOT_FOUND). |
| supplier.id | integer | opcional | Id interno del proveedor. |
| supplier.name | string | opcional | Razón social del proveedor. |
| supplier.tax_id | string | opcional | CUIT/RUT del proveedor. |
| supplier.erp_code | string | opcional | Código ERP del proveedor para tu organización. |
| supplier_name | string | opcional | Nombre del proveedor tal como se cargó (puede diferir de supplier.name si vino solo como texto libre). |
| material_code | string | opcional | Código del material o ítem. |
| description | string | opcional | Descripción del ítem. |
| quantity_ordered | float | opcional | Cantidad pedida. |
| quantity_pending | float | opcional | Cantidad pendiente de entrega. |
| unit_of_measure | string | opcional | Unidad de medida. |
| po_date | date | opcional | Fecha de la OC. |
| original_delivery_date | date | opcional | Fecha de entrega comprometida originalmente. |
| eta | date | opcional | Fecha estimada de entrega. La completa un comprador a mano desde la app (el modal de detalle); ningún proceso automático la calcula. En una línea creada por API queda null hasta que alguien la carga. |
| actual_delivery_date | date | opcional | Fecha real de entrega, cuando el estado es DELIVERED. |
| received_quantity | float | opcional | Cantidad recibida, cuando el estado es DELIVERED. |
| status | string | opcional | Estado público (ver la tabla de estados). |
| is_urgent | boolean | requerido | Si la línea está marcada como urgente. |
| paused | boolean | requerido | Si el envío de seguimiento está pausado (sending_paused). |
| pause_reason | string | opcional | Motivo de la pausa. null si no está pausada. |
| followup_count | integer | requerido | Cantidad de recordatorios enviados en el ciclo actual. |
| last_followup_sent_at | datetime | opcional | Fecha y hora del último recordatorio enviado. |
| next_followup_date | datetime | opcional | Fecha planificada para el próximo recordatorio. |
| supplier_response | object | requerido | Última respuesta del proveedor. |
| supplier_response.responded_at | datetime | opcional | Fecha y hora en que respondió el proveedor. |
| supplier_response.confirmed_delivery_date | date | opcional | Fecha de entrega que confirmó el proveedor. |
| supplier_response.delay_reason | string | opcional | Motivo de la demora, si el proveedor lo indicó. |
| supplier_response.notes | string | opcional | Notas de la última respuesta del proveedor. null si todavía no respondió. |
| supplier_response.quality | string | opcional | Calidad de la respuesta interpretada por la IA (por ejemplo, concrete). |
| created_at | datetime | opcional | Fecha de creación del seguimiento. |
| updated_at | datetime | opcional | Fecha de la última modificación. |
{
"data": [
{
"id": 8821,
"po_code": "OC-2026-0455",
"line_position": 10,
"source": "api",
"supplier": {
"id": 42,
"name": "Distribuidora Industrial SA",
"tax_id": "30712345678",
"erp_code": "PROV-CAL-00123"
},
"supplier_name": "Distribuidora Industrial SA",
"material_code": "MAT-001234",
"description": "Rodamiento SKF 6205",
"quantity_ordered": 50,
"quantity_pending": 20,
"unit_of_measure": "UN",
"po_date": null,
"original_delivery_date": "2026-10-15",
"eta": "2026-10-18",
"actual_delivery_date": null,
"received_quantity": null,
"status": "CONFIRMED_DELAYED",
"is_urgent": false,
"paused": false,
"pause_reason": null,
"followup_count": 2,
"last_followup_sent_at": "2026-09-10T13:05:00+00:00",
"next_followup_date": "2026-09-17T00:00:00+00:00",
"supplier_response": {
"responded_at": "2026-09-12T09:40:00+00:00",
"confirmed_delivery_date": "2026-10-18",
"delay_reason": "Demora del fabricante",
"notes": "Se despachó el lote parcial, el resto llega la semana próxima.",
"quality": "concrete"
},
"created_at": "2026-09-01T12:00:00+00:00",
"updated_at": "2026-09-12T09:40:00+00:00"
}
],
"total": 1,
"page": 1,
"page_size": 50,
"has_more": false
}Obtener un seguimiento de entrega
/api/v1/delivery-trackings/{id}API Key requeridaObtener el detalle de un seguimiento por su id interno.
Misma forma que cada elemento de GET /delivery-trackings.
curl -H "X-API-Key: sk_live_abc123..." \
https://api.gosourced.ai/api/v1/delivery-trackings/8821{
"id": 8821,
"po_code": "OC-2026-0455",
"line_position": 10,
"source": "api",
"supplier": {
"id": 42,
"name": "Distribuidora Industrial SA",
"tax_id": "30712345678",
"erp_code": "PROV-CAL-00123"
},
"supplier_name": "Distribuidora Industrial SA",
"material_code": "MAT-001234",
"description": "Rodamiento SKF 6205",
"quantity_ordered": 50,
"quantity_pending": 20,
"unit_of_measure": "UN",
"po_date": null,
"original_delivery_date": "2026-10-15",
"eta": "2026-10-18",
"actual_delivery_date": null,
"received_quantity": null,
"status": "CONFIRMED_DELAYED",
"is_urgent": false,
"paused": false,
"pause_reason": null,
"followup_count": 2,
"last_followup_sent_at": "2026-09-10T13:05:00+00:00",
"next_followup_date": "2026-09-17T00:00:00+00:00",
"supplier_response": {
"responded_at": "2026-09-12T09:40:00+00:00",
"confirmed_delivery_date": "2026-10-18",
"delay_reason": "Demora del fabricante",
"notes": "Se despachó el lote parcial, el resto llega la semana próxima.",
"quality": "concrete"
},
"created_at": "2026-09-01T12:00:00+00:00",
"updated_at": "2026-09-12T09:40:00+00:00"
}Un id que no existe, o que pertenece a otra organización, devuelve 404 dlvTrackingNotFound: nunca se confirma la existencia de un seguimiento ajeno.
Historial de eventos de un seguimiento
/api/v1/delivery-trackings/{id}/eventsAPI Key requeridaLínea de tiempo paginada de los eventos de un seguimiento, del más antiguo al más reciente.
Parámetros de consulta
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| page | integer | opcional | Número de página (por defecto: 1). |
| page_size | integer | opcional | Items por página, 1-200 (por defecto: 50). |
Objeto de evento
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| id | integer | requerido | Id interno del evento. |
| type | string | requerido | Tipo de evento (ver la tabla de tipos). |
| occurred_at | datetime | opcional | Fecha y hora en que ocurrió. |
| actor_type | string | requerido | Quién lo generó: system, supplier, user o api. |
| data | object | requerido | Detalle del evento; la forma depende de type (ver la tabla de tipos). |
Tipos de evento
| Tipo | data |
|---|---|
| created | { source } |
| reactivated | { source } (el path de la API también agrega from con el estado anterior) |
| cycle_reset | { reason? } — reinicio del ciclo de seguimiento sin reactivación (cambio de proveedor por API, reinicio manual desde la app): la respuesta del proveedor anterior a este evento deja de ser la vigente |
| line_updated | { <campo>: { from, to } } — una clave por cada campo modificado (excepción: buyer_email solo lleva to, sin from) |
| followup_sent | { followup_number, escalated } |
| response_received | { confirmed_delivery_date, delay_reason, notes, quality, summary, outcome } |
| status_changed | { from, to, reason? } |
| paused | { reason } |
| resumed | { reason? } |
| urgency_changed | { is_urgent } |
| delivered | { from, to, reason?, actual_delivery_date, received_quantity } |
| cancelled | { from, to, reason? } |
{
"data": [
{
"id": 55009,
"type": "created",
"occurred_at": "2026-09-01T12:00:00+00:00",
"actor_type": "api",
"data": { "source": "api" }
},
{
"id": 55012,
"type": "status_changed",
"occurred_at": "2026-09-12T09:40:00+00:00",
"actor_type": "supplier",
"data": {
"from": "SENT_PENDING_RESPONSE",
"to": "CONFIRMED_DELAYED",
"reason": "supplier_response"
}
}
],
"total": 2,
"page": 1,
"page_size": 50,
"has_more": false
}Acciones en bloque
/api/v1/delivery-trackings/actionsAPI Key requeridaAplicar una acción a hasta 200 líneas a la vez. Si se omite line_position en un target, la acción se aplica a todas las líneas abiertas de esa OC.
Cuerpo de la solicitud
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| action | string | requerido | mark_delivered, cancel, pause, resume, set_urgent o unset_urgent. |
| targets | array | requerido | Líneas a las que aplicar la acción. Entre 1 y 200. |
| targets[].po_code | string | requerido | Número de OC. Máximo 100 caracteres. |
| targets[].line_position | integer | opcional | Posición de línea. Si se omite, aplica a todas las líneas abiertas de la OC. |
| reason | string | opcional | Motivo. Obligatorio para pause; opcional para el resto. Máximo 500 caracteres. |
| actual_delivery_date | date | opcional | Fecha real de entrega. Solo se usa con mark_delivered; por defecto, hoy. |
| received_quantity | float | opcional | Cantidad recibida. Solo se usa con mark_delivered. ≥ 0. |
Acciones disponibles
| action | Efecto | Requiere |
|---|---|---|
| mark_delivered | Pasa a DELIVERED. actual_delivery_date usa la fecha enviada o, si no se envía, la fecha de hoy de la organización. received_quantity queda registrada si se envía. | — |
| cancel | Pasa a CANCELLED. | — |
| pause | Pausa el envío de recordatorios (sending_paused = true). | reason |
| resume | Reanuda el envío de recordatorios (sending_paused = false). | — |
| set_urgent | Marca la línea como urgente. | — |
| unset_urgent | Desmarca la línea como urgente. | — |
- Las acciones son idempotentes: repetir una acción ya aplicada no escribe un evento nuevo y devuelve uno de estos seis resultados: already_delivered, already_cancelled, already_paused, already_active (para resume), already_urgent o already_not_urgent.
- No se puede cruzar entre estados terminales: mark_delivered sobre una línea CANCELLED, o cancel sobre una línea DELIVERED, devuelve el error dlvActionNotAllowedInStatus. Para reabrir una línea cerrada hay que reenviarla por POST /delivery-trackings (el upsert sí reactiva).
- mark_delivered y cancel solo aplican a líneas creadas por esta API (source = "api"); sobre líneas de CSV o de la sincronización ERP devuelven dlvTrackingManagedByOtherSource. pause, resume, set_urgent y unset_urgent aplican a líneas de cualquier fuente, siempre que no estén en un estado terminal.
curl -X POST https://api.gosourced.ai/api/v1/delivery-trackings/actions \
-H "X-API-Key: sk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"action": "mark_delivered",
"targets": [
{ "po_code": "OC-2026-0455", "line_position": 10 }
],
"received_quantity": 50
}'Respuesta de las acciones
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| applied_count | integer | requerido | Cantidad de seguimientos (no de targets del request) sobre los que se aplicó la acción. Los resultados already_* no suman acá. |
| error_count | integer | requerido | Cantidad de seguimientos (no de targets del request) con error. Los resultados already_* no suman acá. |
| results | array | requerido | Resultado de cada target. |
Objeto de resultado por target (results[])
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| po_code | string | requerido | Número de OC. |
| line_position | integer | opcional | Posición de línea afectada. Puede diferir del target si se omitió (una OC entera puede generar varios resultados). |
| id | integer | opcional | Id interno del seguimiento. Ausente cuando el target no matchea ningún seguimiento. |
| result | string | requerido | applied, error, o uno de los seis resultados idempotentes: already_delivered, already_cancelled, already_paused, already_active, already_urgent, already_not_urgent. |
| status | string | opcional | Estado del seguimiento después de la acción. |
| code | string | opcional | Código del error (dlv*). Solo presente cuando result es error. |
| message | string | opcional | Detalle del error en inglés, pensado para logs. |
// 200 OK
{
"applied_count": 1,
"error_count": 0,
"results": [
{
"po_code": "OC-2026-0455",
"line_position": 10,
"id": 8821,
"status": "DELIVERED",
"result": "applied"
}
]
}Códigos de error — seguimiento de entregas
Códigos propios de los endpoints de seguimiento de entregas. Cinco son errores de la solicitud completa (columna HTTP 400/403/404); el resto viaja en el campo code de cada línea o target con error, dentro de una respuesta 200. Un campo obligatorio ausente o inválido (descripción, cantidades, proveedor sin código ERP ni CUIT, pendiente mayor que pedida) no tiene código propio: rechaza el batch completo con 422 y el detalle estándar de validación.
| Código | HTTP | Significado |
|---|---|---|
| dlvFeatureDisabled | 403 | El seguimiento de entregas no está habilitado para esta organización. |
| dlvTrackingNotFound | 404 | No se encontró el seguimiento (id inexistente o de otra organización). |
| dlvTrackingManagedByOtherSource | 200 | Esta línea la administra otra fuente (CSV o sincronización ERP). |
| dlvSupplierReferenceConflict | 200 | El código ERP y el CUIT corresponden a proveedores distintos. |
| dlvSupplierReferenceAmbiguous | 200 | El código ERP o el CUIT corresponde a más de un proveedor. |
| dlvDuplicateLineInBatch | 200 | La línea (mismo po_code y line_position) aparece más de una vez en el envío. |
| dlvNothingPending | 200 | La línea no tiene nada pendiente (quantity_pending = 0): no se crea un seguimiento para reclamar 0 unidades. |
| dlvConcurrentUpsert | 200 | La línea se modificó al mismo tiempo desde otro envío. Reintentá. |
| dlvLineWriteFailed | 200 | No se pudo guardar la línea. Revisá los datos e intentá de nuevo (no es un problema de concurrencia). |
| dlvInvalidStatusFilter | 400 | El valor de status no es un estado válido. |
| dlvInvalidSince | 400 | Fecha inválida en since. Usá formato ISO 8601. |
| dlvPauseReasonRequired | 400 | Para pause hay que indicar un reason. |
| dlvActionNotAllowedInStatus | 200 | La acción no aplica al estado actual del seguimiento. |
dlvTrackingNotFound es las dos cosas: 404 en los GET por id, y por target dentro de /actions.Webhooks
Recibí eventos en tiempo real cuando cambia una orden de compra, una requisición o un seguimiento de entrega, en vez de tener que consultarlos por polling.
Configuración
Los webhooks se configuran desde la aplicación (no por API key), con un usuario organization_admin, en Mi Organización → Desarrolladores → Webhooks.
Envelope
Todo evento llega con esta forma:
{
"id": "evt_5f2a1c9b8e7d4a3f1b2c",
"type": "delivery_tracking.status_changed",
"created_at": "2026-09-17T14:05:00+00:00",
"data": {
"tracking": "... mismo objeto que devuelve GET /delivery-trackings/{id} ...",
"event": "... mismo objeto que devuelve GET /delivery-trackings/{id}/events ..."
}
}Headers
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| X-Webhook-Signature | string | requerido | Firma HMAC-SHA256 del cuerpo crudo de la solicitud, con el prefijo sha256=. |
| X-Webhook-Event | string | requerido | El type del evento — el mismo valor que el campo type de nivel superior del envelope. No existe data.type: data es directamente el recurso (o el par tracking / event en los eventos de seguimiento). |
| X-Webhook-Delivery-Id | string | requerido | Id del evento, no de este intento de entrega en particular: es el mismo valor en cada reintento y en cada webhook suscrito que lo recibe (igual que id en el cuerpo) — por eso sirve para deduplicar. |
Verificar la firma
Calculá el HMAC-SHA256 sobre los bytes crudos del cuerpo de la solicitud (antes de parsear el JSON) usando el secret del webhook, y compará con X-Webhook-Signature con una comparación de tiempo constante.
import hashlib
import hmac
def verify_webhook(secret: str, raw_body: bytes, signature_header: str | None) -> bool:
if not signature_header or not signature_header.startswith("sha256="):
return False
expected = "sha256=" + hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
# compare_digest on two strings of different length returns False,
# it never raises — safe even with a malformed header.
return hmac.compare_digest(expected, signature_header)
const crypto = require("crypto");
function verifyWebhook(secret, rawBody, signatureHeader) {
if (!signatureHeader || !signatureHeader.startsWith("sha256=")) {
return false;
}
const expected =
"sha256=" +
crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const expectedBuf = Buffer.from(expected);
const receivedBuf = Buffer.from(signatureHeader);
// timingSafeEqual throws on a length mismatch — check first, it never
// needs to be constant-time (the lengths themselves aren't a secret).
if (expectedBuf.length !== receivedBuf.length) {
return false;
}
return crypto.timingSafeEqual(expectedBuf, receivedBuf);
}
Reintentos
Si la entrega falla (timeout, error de red o una respuesta que no sea 2xx), se reintenta hasta 3 veces más (4 intentos en total: el envío inicial más 3 reintentos). Cada intento fallido programa el siguiente con un backoff mínimo de 1, 5 y 15 minutos; ese reintento se dispara recién la próxima vez que corre el barrido de reintentos (reminder-checker), que se ejecuta cada hora en horario laboral argentino — el tiempo real hasta el próximo intento puede ser mayor a esos minutos.
- Intento 1: inmediato, al momento del evento.
- Intento 2: no antes de 1 minuto después.
- Intento 3: no antes de 5 minutos después.
- Intento 4: no antes de 15 minutos después. Si también falla, la entrega queda FAILED.
Eventos disponibles
| type | data | Cuándo se dispara |
|---|---|---|
| purchase_order.created | objeto de la OC | Se creó una nueva orden de compra. |
| purchase_order.updated | id, code, status, reason | Cambió el estado u otro dato de una orden de compra (por ejemplo, un rechazo). |
| requisition.discarded | external_id, requisition_id, discarded_by, discarded_at | Se descartó una requisición (por ejemplo, cancelada desde el ERP con POST /requisitions/cancel). |
| delivery_tracking.response_received | tracking, event | El proveedor respondió el pedido de confirmación de entrega. |
| delivery_tracking.status_changed | tracking, event | Cambió el estado del seguimiento (incluye la reactivación de una línea previamente cerrada, que vuelve a PENDING_SCHEDULE). |
| delivery_tracking.delivered | tracking, event | El seguimiento pasó a DELIVERED. |
| delivery_tracking.cancelled | tracking, event | El seguimiento pasó a CANCELLED. |
Referencia de esquemas
Referencia rápida de todos los esquemas de solicitud/respuesta usados en los endpoints.
Estados de requisición
| Estado | Descripción |
|---|---|
| PENDING | Importada, esperando revisión de un comprador en Sourced. |
| LAUNCHED | El comprador lanzó la requisición como Solicitud de Compra. |
| DISCARDED | La requisición fue descartada manualmente. |
| SUPERSEDED | Se importó una versión más nueva con el mismo external_id. |
Estados de orden de compra
| Estado | Descripción |
|---|---|
| DRAFT | OC creada en Sourced, pendiente de sincronización al ERP. |
| CREATED | OC creada en el ERP externo, pendiente de aprobación. |
| CONFIRMED | OC aprobada completamente en el ERP externo. |
| REJECTED | OC rechazada en el ERP externo. La PR asociada se reabre automáticamente. |
| CANCELLED | Compra caída. La OC se cancela y la PR asociada también - nada se reabre. |
Códigos de referencia
Catálogos de monedas y tipos de pago aceptados por la API. Usalos para mapear estos valores con los códigos equivalentes en tu ERP.
Monedas
Sourced no usa un enum cerrado de monedas: cualquier código ISO 4217 válido de 3 letras es aceptado. Las monedas más utilizadas por nuestros clientes son:
| Código | Moneda |
|---|---|
| ARS | Peso argentino |
| USD | Dólar estadounidense |
| EUR | Euro |
| BRL | Real brasileño |
Tipos de pago (payment_terms_code)
Catálogo global de condiciones de pago disponibles en Sourced. Usá el código (columna Code) en el campo payment_terms_code de la PO. El campo type indica la naturaleza del pago: IMMEDIATE (al recibir), ADVANCE (anticipado), NET_DAYS (a X días de la factura).
| Código | Nombre | Tipo | Días | Descripción |
|---|---|---|---|---|
| COD | Cash On Delivery | IMMEDIATE | 0 | Pago contra entrega |
| IMM | Immediate Payment | IMMEDIATE | 0 | Pago inmediato / Contado |
| ADV100 | Advance Payment | ADVANCE | 0 | Pago por adelantado 100% |
| ADV50 | Advance 50% | ADVANCE | 0 | Pago por adelantado del 50% |
| NET7 | Net 7 Days | NET_DAYS | 7 | Pago a 7 días de la fecha de factura |
| NET10 | Net 10 Days | NET_DAYS | 10 | Pago a 10 días de la fecha de factura |
| NET15 | Net 15 Days | NET_DAYS | 15 | Pago a 15 días de la fecha de factura |
| NET20 | Net 20 Days | NET_DAYS | 20 | Pago a 20 días de la fecha de factura |
| NET21 | Net 21 Days | NET_DAYS | 21 | Pago a 21 días de la fecha de factura |
| NET30 | Net 30 Days | NET_DAYS | 30 | Pago a 30 días de la fecha de factura |
| EOM30 | End of Month + 30 | NET_DAYS | 30 | Pago a fin de mes más 30 días |
| NET35 | Net 35 Days | NET_DAYS | 35 | Pago a 35 días de la fecha de factura |
| NET40 | Net 40 Days | NET_DAYS | 40 | Pago a 40 días de la fecha de factura |
| NET45 | Net 45 Days | NET_DAYS | 45 | Pago a 45 días de la fecha de factura |
| NET60 | Net 60 Days | NET_DAYS | 60 | Pago a 60 días de la fecha de factura |
| NET75 | Net 75 Days | NET_DAYS | 75 | Pago a 75 días de la fecha de factura |
| NET90 | Net 90 Days | NET_DAYS | 90 | Pago a 90 días de la fecha de factura |
Unidades de medida
Códigos de unidad de medida aceptados en items de requisiciones. Se envían en el campo unit_of_measure.
| Código | Unidad |
|---|---|
| EA | Unidad |
| PCS | Piezas |
| KG | Kilogramo |
| G | Gramo |
| LB | Libra |
| OZ | Onza |
| M | Metro |
| CM | Centímetro |
| MM | Milímetro |
| IN | Pulgada |
| FT | Pie |
| YD | Yarda |
| L | Litro |
| ML | Mililitro |
| GAL | Galón |
| QT | Cuarto de galón |
| PT | Pinta |
| FL_OZ | Onza líquida |
| M2 | Metro cuadrado |
| CM2 | Centímetro cuadrado |
| FT2 | Pie cuadrado |
| IN2 | Pulgada cuadrada |
| YD2 | Yarda cuadrada |
| M3 | Metro cúbico |
| CM3 | Centímetro cúbico |
| FT3 | Pie cúbico |
| IN3 | Pulgada cúbica |
| YD3 | Yarda cúbica |
| BOX | Caja |
| CASE | Caja/Estuche |
| PACK | Paquete |
| SET | Juego |
| KIT | Kit |
| BUNDLE | Manojo |
| ROLL | Rollo |
| SHEET | Hoja/Lámina |
| PALLET | Paleta |
| DRUM | Tambor/Bidón |
| BAG | Bolsa |
| BOTTLE | Botella |
| CEN | Centena |
Flujo de integración
Patrón de integración típico para una sincronización ERP programada (ej: cron job cada 15 minutos):
Recopilar nuevas requisiciones del ERP
Consultar tu ERP por requisiciones aprobadas que aún no fueron sincronizadas con Sourced.
Verificar existentes (opcional)
Llamar a POST /requisitions/check-existing con external_ids para filtrar requisiciones ya sincronizadas.
Importar requisiciones
Llamar a POST /requisitions con el lote de requisiciones nuevas/actualizadas. Guardar los requisition_ids devueltos.
Consultar órdenes de compra nuevas
Llamar a GET /purchase-orders?since={last_sync_timestamp}&status=DRAFT para obtener OC nuevas. Crear la OC en tu ERP y llamar a POST /purchase-orders/{id}/status con {"status": "CREATED"}.
Confirmar OC aprobadas
Cuando la OC se apruebe en tu ERP, llamar a POST /purchase-orders/{id}/status con {"status": "CONFIRMED"} para cerrar el ciclo.
# Step 1: Get pending requisitions from ERP
ERP_REQS=$(query_erp_pending_requisitions)
# Step 2: Check which ones already exist in Sourced
EXISTING=$(curl -s -X POST \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d "{"external_ids": $ERP_REQ_IDS}" \
https://api.gosourced.ai/api/v1/requisitions/check-existing)
# Step 3: Import only new requisitions
NEW_REQS=$(filter_not_found $ERP_REQS $EXISTING)
curl -s -X POST \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d "{"requisitions": $NEW_REQS}" \
https://api.gosourced.ai/api/v1/requisitions
# Step 4: Pull new POs since last sync
POS=$(curl -s \
-H "X-API-Key: $API_KEY" \
"https://api.gosourced.ai/api/v1/purchase-orders?since=$LAST_SYNC&status=DRAFT")
# Step 5: Create POs in ERP and update status
for PO in $POS; do
create_po_in_erp $PO
curl -s -X POST \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d "{"status": "CREATED", "external_id": "$ERP_PO_NUMBER"}" \
"https://api.gosourced.ai/api/v1/purchase-orders/$PO_ID/status"
done
# Step 6: When PO is approved in ERP, confirm it
curl -s -X POST \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "CONFIRMED"}' \
"https://api.gosourced.ai/api/v1/purchase-orders/$PO_ID/status"Ejemplo completo
Una solicitud de importación realista con todos los campos disponibles:
curl -X POST https://api.gosourced.ai/api/v1/requisitions \
-H "X-API-Key: sk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"requisitions": [
{
"external_id": "CAL-REQ-2026-0042",
"description": "Repuestos bomba centrífuga Planta Norte",
"requester_name": "Carlos Rodríguez",
"requester_email": "carlos.rodriguez@empresa.com",
"comments": "Urgente: bomba fuera de servicio desde 03/03. <b>Necesitamos entrega express.</b>",
"delivery_address": "Av. Industrial 4500, Parque Industrial Pilar, Buenos Aires",
"delivery_address_code": "PLANTA-NORTE",
"desired_delivery_lead_time_days": 7,
"created_date": "2026-03-04T09:15:00Z",
"offer_deadline": "2026-03-10T18:00:00Z",
"total_estimated_value": 1250000.00,
"currency": "ARS",
"department_code": "MANT-001",
"priority_level": "HIGH",
"items": [
{
"external_line_id": "CAL-REQ-2026-0042-L10",
"description": "Sello mecánico para bomba centrífuga KSB ETA 50-200",
"quantity": 2,
"unit_of_measure": "UN",
"target_price": 185000.00,
"estimated_price": 370000.00,
"currency": "ARS",
"category": "Repuestos Bombas",
"material_code": "MAT-BOM-0234",
"desired_delivery_lead_time_days": 10,
"detail": "Sello tipo cartucho, material: carburo de silicio / carburo de silicio",
"specifications": {
"manufacturer_code": "KSB-SEAL-50200",
"manufacturer_name": "KSB",
"manufacturer_description": "Mechanical seal for ETA 50-200 centrifugal pump",
"buyer_code": "MAT-BOM-0234",
"buyer_code_description": "Sello mecánico bomba KSB ETA 50-200",
"buyer_code_system": "Calipso",
"technical_specs": "Diámetro eje: 35mm, Material caras: SiC/SiC, Elastómeros: Viton",
"requirements": "Debe incluir certificado de calidad. Preferencia por repuesto original KSB."
}
},
{
"external_line_id": "CAL-REQ-2026-0042-L20",
"description": "Rodamiento SKF 6310-2RS",
"quantity": 4,
"unit_of_measure": "UN",
"target_price": 45000.00,
"currency": "ARS",
"category": "Rodamientos",
"material_code": "MAT-ROD-0089",
"specifications": {
"manufacturer_code": "6310-2RS1",
"manufacturer_name": "SKF",
"technical_specs": "50x110x27mm, sellado ambos lados, grasa estándar"
}
},
{
"external_line_id": "CAL-REQ-2026-0042-L30",
"description": "Aceite lubricante ISO VG 68",
"quantity": 20,
"unit_of_measure": "LT",
"target_price": 5500.00,
"currency": "ARS",
"category": "Lubricantes",
"specifications": {
"technical_specs": "Aceite mineral ISO VG 68, índice de viscosidad > 95"
}
}
],
"attachments": [
{
"url": "https://erp.empresa.com/files/plano-bomba-eta-50-200.pdf",
"filename": "plano_bomba_KSB_ETA_50-200.pdf",
"description": "Plano de despiece bomba KSB ETA 50-200",
"file_type": "application/pdf"
}
],
"raw_data": {
"calipso_doc_type": "SOL",
"calipso_doc_number": "0042",
"calipso_branch": "001",
"approved_by": "María González",
"cost_center": "CC-MANT-NORTE"
}
}
]
}'{
"success": true,
"created_count": 1,
"updated_count": 0,
"skipped_count": 0,
"error_count": 0,
"errors": [],
"requisition_ids": [1234]
}