API de datasets
Un dataset es una colección de conversaciones en ChatML lista para entrenar. Con la API creas datasets, importas archivos y lees sus ejemplos.
Rutas
Las rutas sin marca aceptan API Key. Las dos de /projects/{projectId}/datasets leen con la sesión de la app: con una API Key responden 404.
Formato de los ejemplos
Cada ejemplo es una conversación en ChatML: un objeto con messages, y cada mensaje con role (system, user, assistant o tool) y content. Los mensajes pueden traer tool_calls y name.
{"messages": [{"role": "system", "content": "Eres el asistente de ventas de una tienda de muebles."}, {"role": "user", "content": "¿Hacen envíos a Monterrey?"}, {"role": "assistant", "content": "Sí, enviamos a todo México. ¿Me compartes tu código postal para cotizar?"}]}
{"messages": [{"role": "system", "content": "Eres el asistente de ventas de una tienda de muebles."}, {"role": "user", "content": "¿Puedo pagar a meses?"}, {"role": "assistant", "content": "Sí, con tarjeta de crédito participante. ¿Qué producto te interesa?"}]}Otros formatos (ShareGPT, Alpaca, pares input/output o prompt/completion) se convierten solos si los importas con /ingest. Los detalles de cada formato están en Formatos de importación.
Crear un dataset con sus ejemplos
curl -X POST https://app.astrapp.lat/api/v1/datasets \
-H "Authorization: Bearer $ASTRAPP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Ventas septiembre",
"description": "Conversaciones reales del chat de ventas",
"format": "chatml",
"items": [
{"messages": [
{"role": "user", "content": "¿Hacen envíos a Monterrey?"},
{"role": "assistant", "content": "Sí, enviamos a todo México."}
]}
]
}'| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
name | string | Sí | Nombre del dataset. |
description | string | No | Descripción libre. |
items | object[] | No | Los ejemplos. Si todos traen messages, se validan como ChatML. |
format | string | No | csv, json, parquet, txt, jsonl o chatml. Cualquier otro valor se guarda como jsonl. |
source | string | No | Origen del dataset. Por defecto manual. |
{
"data": {
"id": "8d2b6f41-0a3c-4e59-b7d8-21f5c9043ae6",
"name": "Ventas septiembre",
"description": "Conversaciones reales del chat de ventas",
"itemCount": 1,
"format": "chatml",
"source": "manual",
"createdAt": "2026-09-22T10:00:00.000Z",
"syncedToOpik": false
},
"message": "Dataset creado exitosamente"
}- El dataset se guarda en el proyecto más antiguo de tu organización; si no tienes ninguno, se crea “Mi primer proyecto”. Esta ruta no recibe
projectId. - Los ejemplos se reparten por posición: 8 de cada 10 a
train, 1 atesty 1 avalidation. - Si todos los items traen
messages, se validan como ChatML y uno inválido hace que la ruta responda 400 con su número. Filas de CSV con un mensaje por fila se agrupan en conversaciones; cualquier otra forma se guarda tal cual, sin convertir. Para convertir otros formatos usa/ingest.
Límite de datasets por plan
"error": "plan_limit_reached", current y limit.Importar un archivo
Es lo mismo que hace el asistente de importación de la app: detecta el formato, lo convierte a ChatML y quita duplicados.
jq -n \
--rawfile text conversaciones.jsonl \
--arg projectId "$PROJECT_ID" \
'{text: $text, fileName: "conversaciones.jsonl", projectId: $projectId, datasetName: "Ventas septiembre"}' \
| curl -X POST https://app.astrapp.lat/api/v1/ingest \
-H "Authorization: Bearer $ASTRAPP_API_KEY" \
-H "Content-Type: application/json" \
-d @-| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
text | string | Sí | El contenido completo del archivo, como texto. |
fileName | string | Sí | Nombre del archivo con extensión; ayuda a detectar el formato. |
projectId | string | Sí | UUID de un proyecto de tu organización. |
datasetName | string | No | Si lo mandas, crea un dataset con las conversaciones importadas. |
dryRun | boolean | No | true analiza y devuelve una vista previa sin guardar nada. |
language | 'es' | 'en' | 'other' | No | Idioma de las conversaciones. Decide los marcadores de la limpieza de datos personales ([TELÉFONO] o [PHONE]). Por defecto es. |
redactPii | boolean | No | Quita datos personales antes de guardar, y antes de calcular el hash de deduplicación. Por API viene apagado; el asistente lo manda prendido. |
parseOptions.businessSender | string | No | Participante que responde por tu negocio en un chat de WhatsApp o WeChat: sus mensajes se guardan como assistant. |
parseOptions.txtMode | 'whatsapp' | 'role_prefix' | 'alternating' | No | Cómo interpretar un TXT que no traiga un formato reconocible por contenido. |
actions.limit | number | No | Máximo de conversaciones a importar. |
actions.splitInto | number | No | Parte el dataset en varios de este tamaño. |
Las conversaciones entran al proyecto y, si mandaste datasetName, se agrupan en un dataset nuevo. Reimportar el mismo archivo no duplica conversaciones. Esta ruta responde sin sobre data:
{
"summary": {
"parsed": 2,
"deduped": 0,
"errorsFlagged": 0,
"detectedFormat": "chatml",
"origin": "imported"
},
"imported": 2,
"created": true,
"datasetId": "5e0c7a91-2b3d-4f6e-8a1c-9d2e3f4a5b6c",
"datasetIds": ["5e0c7a91-2b3d-4f6e-8a1c-9d2e3f4a5b6c"],
"conversationIds": [
"1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e"
],
"dedupedAgainstPool": 0,
"limitedByPlan": 0
}Cuántas conversaciones entran por importación
| Plan | Conversaciones por importación |
|---|---|
| Gratuito | 100 |
| Starter | 500 |
| Growth | 1,500 |
| Professional | Sin límite |
Si el archivo trae más, se importan las primeras y limitedByPlan dice cuántas quedaron fuera. La ruta admite 10 peticiones por minuto por IP.
Subir un archivo para entrenar
/datasets/upload guarda un archivo en tu almacenamiento y te devuelve su ruta; no crea un dataset. Esa ruta es la que mandas como datasetStoragePath al lanzar un entrenamiento. Acepta multipart/form-data con el campo file y las extensiones json, jsonl, csv, tsv, txt y parquet.
curl -X POST https://app.astrapp.lat/api/v1/datasets/upload \
-H "Authorization: Bearer $ASTRAPP_API_KEY" \
-F "[email protected]"{
"success": true,
"data": {
"storagePath": "<tu userId>/datasets/1758535200000-conversaciones.jsonl",
"sizeBytes": 20480,
"filename": "conversaciones.jsonl"
}
}El límite real del cuerpo es de unos 4.5 MB
Consultar datasets
Listar
curl "https://app.astrapp.lat/api/v1/datasets?project_id=$PROJECT_ID" \
-H "Authorization: Bearer $ASTRAPP_API_KEY"Devuelve { "data": [ … ] } sin paginación. project_id es opcional. El número de ejemplos viene en item_count y también en conversation_count: son el mismo valor con dos nombres, no dos métricas.
Un dataset
{
"data": {
"id": "8d2b6f41-0a3c-4e59-b7d8-21f5c9043ae6",
"name": "Ventas septiembre",
"description": "Conversaciones reales del chat de ventas",
"format": "chatml",
"source": "manual",
"totalItems": 150,
"metadata": {},
"huggingfaceId": null,
"huggingfaceConfig": null,
"huggingfaceSplit": null,
"projectId": "3f1a9c2e-5b74-4d81-9e02-7c6a1b4f8d35",
"createdAt": "2026-09-22T10:00:00.000Z",
"updatedAt": "2026-09-22T10:05:00.000Z",
"splits": { "train": 120, "test": 15, "validation": 15 }
}
}Sus ejemplos
curl "https://app.astrapp.lat/api/v1/datasets/$DATASET_ID/items?limit=100&offset=0&split=train" \
-H "Authorization: Bearer $ASTRAPP_API_KEY"limit va de 50 por defecto a 5,000 como máximo; split es opcional. Cada item trae id, index, split y messages, y la respuesta incluye meta con total, limit, offset y split.
También acepta los filtros de la lista de Conversaciones: search, qa_status (pending, valid, invalid o skipped), has_tools (true o false) y message_length (short, 3 mensajes o menos sin el system, o long). Valen igual para los ejemplos que son referencia a una conversación del proyecto. En un dataset muy grande, un filtro que tarda demasiado responde 503 con el código QUERY_TIMEOUT.
Actualizar y eliminar
PUT /datasets/{datasetId} acepta name, description y metadata; la metadata nueva se combina con la que ya existe. DELETE borra el dataset con todos sus ejemplos de forma permanente y responde { "success": true }.
Ambas rutas, igual que la consulta, verifican que el dataset sea de un proyecto de tu organización o que lo hayas creado tú; si no, responden 403.
Antes de entrenar