Crear transacciones en lote
curl --request POST \
--url http://api.gu1.ai/transactions/batch \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"transactions": [
{}
],
"executeRules": true,
"skipDuplicates": true,
"linkEntityStrict": true,
"validateExistingEntity": true,
"sources": [
{}
]
}
'import requests
url = "http://api.gu1.ai/transactions/batch"
payload = {
"transactions": [{}],
"executeRules": True,
"skipDuplicates": True,
"linkEntityStrict": True,
"validateExistingEntity": True,
"sources": [{}]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
transactions: [{}],
executeRules: true,
skipDuplicates: true,
linkEntityStrict: true,
validateExistingEntity: true,
sources: [{}]
})
};
fetch('http://api.gu1.ai/transactions/batch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "http://api.gu1.ai/transactions/batch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'transactions' => [
[
]
],
'executeRules' => true,
'skipDuplicates' => true,
'linkEntityStrict' => true,
'validateExistingEntity' => true,
'sources' => [
[
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "http://api.gu1.ai/transactions/batch"
payload := strings.NewReader("{\n \"transactions\": [\n {}\n ],\n \"executeRules\": true,\n \"skipDuplicates\": true,\n \"linkEntityStrict\": true,\n \"validateExistingEntity\": true,\n \"sources\": [\n {}\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("http://api.gu1.ai/transactions/batch")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"transactions\": [\n {}\n ],\n \"executeRules\": true,\n \"skipDuplicates\": true,\n \"linkEntityStrict\": true,\n \"validateExistingEntity\": true,\n \"sources\": [\n {}\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("http://api.gu1.ai/transactions/batch")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"transactions\": [\n {}\n ],\n \"executeRules\": true,\n \"skipDuplicates\": true,\n \"linkEntityStrict\": true,\n \"validateExistingEntity\": true,\n \"sources\": [\n {}\n ]\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"created": 123,
"skipped": 123,
"failed": 123,
"transactions": [
{}
],
"errors": [
{}
],
"processingTime": "<string>",
"jobId": "<string>",
"status": "<string>",
"message": "<string>",
"fileCount": 123
}Referencia API
Crear transacciones en lote
Crear múltiples transacciones en una sola llamada para escenarios de alto volumen — en la API de monitoreo transaccional gu1 para fraude y AML.
POST
/
transactions
/
batch
Crear transacciones en lote
curl --request POST \
--url http://api.gu1.ai/transactions/batch \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"transactions": [
{}
],
"executeRules": true,
"skipDuplicates": true,
"linkEntityStrict": true,
"validateExistingEntity": true,
"sources": [
{}
]
}
'import requests
url = "http://api.gu1.ai/transactions/batch"
payload = {
"transactions": [{}],
"executeRules": True,
"skipDuplicates": True,
"linkEntityStrict": True,
"validateExistingEntity": True,
"sources": [{}]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
transactions: [{}],
executeRules: true,
skipDuplicates: true,
linkEntityStrict: true,
validateExistingEntity: true,
sources: [{}]
})
};
fetch('http://api.gu1.ai/transactions/batch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "http://api.gu1.ai/transactions/batch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'transactions' => [
[
]
],
'executeRules' => true,
'skipDuplicates' => true,
'linkEntityStrict' => true,
'validateExistingEntity' => true,
'sources' => [
[
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "http://api.gu1.ai/transactions/batch"
payload := strings.NewReader("{\n \"transactions\": [\n {}\n ],\n \"executeRules\": true,\n \"skipDuplicates\": true,\n \"linkEntityStrict\": true,\n \"validateExistingEntity\": true,\n \"sources\": [\n {}\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("http://api.gu1.ai/transactions/batch")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"transactions\": [\n {}\n ],\n \"executeRules\": true,\n \"skipDuplicates\": true,\n \"linkEntityStrict\": true,\n \"validateExistingEntity\": true,\n \"sources\": [\n {}\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("http://api.gu1.ai/transactions/batch")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"transactions\": [\n {}\n ],\n \"executeRules\": true,\n \"skipDuplicates\": true,\n \"linkEntityStrict\": true,\n \"validateExistingEntity\": true,\n \"sources\": [\n {}\n ]\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"created": 123,
"skipped": 123,
"failed": 123,
"transactions": [
{}
],
"errors": [
{}
],
"processingTime": "<string>",
"jobId": "<string>",
"status": "<string>",
"message": "<string>",
"fileCount": 123
}Resumen
Crea múltiples transacciones en una sola operación en lote. Este endpoint está optimizado para alto volumen. Límites:- Un solo lote: hasta 100.000 transacciones por petición; el cuerpo no debe superar 50 MB.
- Varios archivos en una llamada: usar
POST /transactions/batch/background-multicon un arraysources(hasta 5 archivos, 100.000 transacciones cada uno); límite del cuerpo 150 MB. - Carga por archivos (multipart): usar
POST /transactions/batch/uploadpara enviar archivos CSV, Excel o JSON directamente; máx. 5 archivos por petición. El límite por archivo depende de tu plan.
- La API mantiene la conexión abierta hasta 30 segundos.
- Si el lote termina en 30 segundos, responde 200 con el resumen completo (creadas, omitidas, reglas ejecutadas, tiempo, etc.).
- Si supera 30 segundos, responde 202 con un
jobIdy sigue procesando en segundo plano. Al terminar se notifica en el dashboard (y por socket en tiempo real si está conectado).
POST /transactions/batch/background— Un solo lote; devuelve 202 de inmediato y notifica por socket al terminar.POST /transactions/batch/background-multi— Varios archivos en una petición (máx. 5); devuelve 202 y una notificación cuando se procesan todos.POST /transactions/batch/upload— Multipart form-data: enviar uno o más archivos (CSV, Excel, JSON) en el campofile; el servidor los parsea y ejecuta el mismo flujo. Ideal cuando tenés archivos en lugar de JSON.
Endpoints
POST http://api.gu1.ai/transactions/batch
POST http://api.gu1.ai/transactions/batch/background
POST http://api.gu1.ai/transactions/batch/background-multi
POST http://api.gu1.ai/transactions/batch/upload (multipart/form-data)
Carga por archivos (multipart)
UsáPOST /transactions/batch/upload cuando tengas archivos CSV, Excel o JSON y quieras que el servidor los parsee. La petición debe ser multipart/form-data (FormData), no JSON.
- Máx. 5 archivos por petición.
- Formatos aceptados: CSV (
.csv), Excel (.xlsx,.xls), JSON (.json). - Límite de transacciones por archivo según el plan de la organización (ver Límites por plan).
- Query:
validateGap(por defectofalse). Si enviásvalidateGap=truecon varios archivos, la API comprueba que no haya un salto de tiempo de 30 minutos o más entre el fin de un archivo y el inicio del siguiente (portransactedAt). Si lo hay, devuelve 400 concode: "GAP_VALIDATION_REQUIRED"y la lista de saltos. Por defecto la validación está desactivada; usá?validateGap=truepara activarla.
Campos del form
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
file | File | Sí | Uno o más archivos (CSV, Excel, JSON). Repetí el campo file para varios archivos. |
executeRules | string | No | "true" (default) o "false" |
skipDuplicates | string | No | "true" (default) o "false" |
linkEntityStrict | string | No | Override opcional: "true" hard-fail / "false" soft-link. Omitir → validateExistingEntity |
validateExistingEntity | string | No | "true" (default si se omite) o "false" |
Ejemplo: envío con FormData (JavaScript)
const form = new FormData();
form.append('file', fileInput.files[0]);
form.append('executeRules', 'true');
form.append('skipDuplicates', 'true');
form.append('validateExistingEntity', 'true'); // default de batch si se omite
const response = await fetch('http://api.gu1.ai/transactions/batch/upload', {
method: 'POST',
headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
body: form // No pongas Content-Type; el navegador setea el boundary multipart
});
const data = await response.json();
Ejemplo: cURL
curl -X POST http://api.gu1.ai/transactions/batch/upload \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@transacciones.csv" \
-F "executeRules=true" \
-F "skipDuplicates=true"
Límites por plan
El máximo de transacciones por archivo (tanto en upload como en background-multi) depende del plan de la organización:| Plan | Máx. transacciones por archivo |
|---|---|
| Freemium | 4.000 |
| Startup | 12.000 |
| Growth | 30.000 |
| Enterprise | 100.000 |
| Por uso (pay-as-you-go) | 100.000 |
Concurrencia
Un lote de transacciones vivo (queued o running) por organización. Un segundo upload de transacciones mientras hay otro de transacciones en curso devuelve 409 JOB_IN_PROGRESS. Si la org ya tiene un lote de entidades en cola, este POST devuelve 409 JOB_ALREADY_WAITING (como máximo un job en espera por organización). Si un lote de entidades está running y nadie espera, este POST devuelve 202 queued (code: "QUEUED_WAITING_PREVIOUS_JOB", queue.waitingForOrgJob: true) y arranca cuando ese job termina. Como máximo 2 organizaciones procesan lotes de transacciones a la vez; las demás reciben 202 con code: "QUEUED_WAITING_SLOT", status: "queued" y un snapshot queue (no 409). El job sigue running hasta que termine la evaluación de reglas encolada si executeRules es true. Ver Importaciones masivas — Concurrencia.
Plantillas
Para armar archivos CSV, Excel o JSON que cumplan el esquema:- Dashboard: En Monitoreo de transacciones, abrí el modal Carga batch. Usá los botones Descargar CSV, Descargar Excel o Descargar JSON para obtener plantillas con los encabezados correctos y una fila de ejemplo. Es la forma más sencilla de empezar.
- Campos requeridos por fila: Cada transacción debe incluir al menos
externalId,type,amountycurrency. Recomendados:transactedAt,status,originName,destinationName,description. Esquema completo en Crear transacción. - CSV/Excel: Los nombres de columna pueden ser camelCase (
externalId,transactedAt) o snake_case (external_id,transacted_at); el servidor los normaliza. Fecha en ISO 8601 (ej.2025-01-15T10:30:00.000Z).
Autenticación
Authorization: Bearer YOUR_API_KEY
Cuerpo (sync y background)
array
required
Array de objetos de transacción (mín. 1, máx. 100.000 por petición). Misma estructura que Crear transacción. Cuerpo máx. 50 MB.
exchangeRate opcional por fila: solo si falla la conversión automática. La tasa automática existe solo para los códigos de Monedas con conversión automática. Orden de resolución: Conversión de moneda.boolean
default:"true"
Si se ejecutan reglas para todas las transacciones del lote
boolean
default:"true"
Omitir transacciones con
externalId duplicado en lugar de fallar todo el loteboolean
Override opcional.
true: hard-fail. false: soft-link. Si se omite, manda validateExistingEntity. También ?linkEntityStrict=true|false.boolean
default:"true"
Default de batch
true (sin cambio para quien omite el campo): refs sin resolver → 400. Con false: auto-vincula si encuentra y sigue sin vínculo si no.*EntityId y, si no enviaste nombre/país, se pueden rellenar *Name y *Country desde la entidad (como en creación individual).
Cuando un extremo queda vinculado (incluso si ya enviaste *EntityId), originTaxId / originExternalId y destinationTaxId / destinationExternalId se sincronizan siempre desde la entidad antes del insert — misma denormalización canónica que Crear transacción — origen.
Cada fila creada también recibe el campo linkedEntityGu1, propiedad del servidor, dentro de originDetails y destinationDetails. Su valor es linked, unresolved o not_requested. Gu1 ignora y sobrescribe cualquier valor incluido en el archivo.
Cuerpo (solo background-multi)
array
required
Array de objetos:
{ "fileName": "opcional", "transactions": [ ... ] }. Mín. 1, máx. 5 fuentes. Cada transactions: máx. 100.000 (o el límite de tu plan por archivo). Cuerpo total máx. 150 MB.Respuesta 200 (completado en 30 s)
boolean
Si la operación en lote se completó correctamente
number
Número de transacciones creadas
number
Omitidas (duplicados o errores de validación)
number
Fallidas
array
Transacciones creadas
array
Errores por transacción:
index, externalId, error, codestring
Tiempo total de procesamiento
Respuesta 202 (procesamiento > 30 s o background)
string
Identificador del job; correlacionar con la notificación en tiempo real al completar
string
"processing"string
Mensaje indicando que el proceso continúa en segundo plano y se notificará en el dashboard
number
(Solo background-multi.) Número de archivos en proceso
Ejemplo de uso
curl -X POST http://api.gu1.ai/transactions/batch \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transactions": [
{
"externalId": "txn_001",
"type": "PAYMENT",
"amount": 50.00,
"currency": "USD",
"originExternalId": "customer_001",
"destinationExternalId": "merchant_100",
"channel": "web_browser"
}
],
"executeRules": true,
"skipDuplicates": true
}'
import requests
response = requests.post(
'http://api.gu1.ai/transactions/batch',
headers={'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json'},
json={
'transactions': transactions,
'executeRules': True,
'skipDuplicates': True
}
)
result = response.json()
print(f"Creadas: {result['created']}, Omitidas: {result['skipped']}, Fallidas: {result['failed']}")
Errores
- 400 - Lote vacío o tamaño excedido (máx. 100.000 por request)
- 413 - Cuerpo > 50 MB (o 150 MB en multi). Dividir en lotes más pequeños.
Buenas prácticas
- Respetar límites: 100.000 por request, 50 MB (150 MB multi).
- Usar siempre
skipDuplicates: true. - Para importación histórica:
executeRules: falsey opcionalmentevalidateExistingEntity: false. - Manejar 200 vs 202: 200 = resultado en el cuerpo; 202 = job en background, esperar notificación.
- Validar campos requeridos según Crear transacción.
- Usar background-multi para muchos archivos (hasta 10 en una llamada).
Próximos pasos
- Crear transacción - Una transacción
- Cambiar estado de transacción - Cambiar estado
- Monitoreo de transacciones - Detección de fraude
Was this page helpful?