Volver a Documentacion

Órdenes FX

Las órdenes de cambio de divisas convierten fondos entre dos monedas. Dos endpoints conducen el flujo:

Endpoint Propósito
POST /api/v1/fxorder/getQuote Obtiene una tasa actual. Opcionalmente la bloquea por 60 segundos y retorna un quote_id.
POST /api/v1/fxorder/createOrder Ejecuta la conversión, opcionalmente anclada a un quote_id previamente bloqueado.
POST /api/v1/fxorder/getStatus Consulta una orden por su código de referencia.
POST /api/v1/fxorder/getHistory Lista las órdenes del socio con paginación.

Para movimientos en la misma moneda, vea Transferencias.


Inicio rápido

# 1. Bloquee una tasa USD → CRC por 60 segundos
QUOTE=$(curl -fsS -X POST "https://sandbox-api.ariari.xyz/api/v1/fxorder/getQuote" \
  -H "Ocp-Apim-Subscription-Key: $SU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "origin_currency": "USD",
    "destination_currency": "CRC",
    "origin_amount": 1000,
    "lock_rate": true
  }')
echo "$QUOTE"
# { "quote_id": "fxq_...", "rate": "512.750000",
#   "origin_amount": 1000, "destination_amount": 512750.00,
#   "fee_amount": 2.5, "fee_currency": "USD",
#   "expires_at": "2026-04-28T10:35:00.000Z", ... }

# 2. Use la cotización bloqueada en createOrder
QUOTE_ID=$(echo "$QUOTE" | jq -r .quote_id)
curl -fsS -X POST "https://sandbox-api.ariari.xyz/api/v1/fxorder/createOrder" \
  -H "Ocp-Apim-Subscription-Key: $SU_CLAVE" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d "{
    \"origin\":      { \"wallet_id\": \"550e8400-e29b-41d4-a716-446655440000\" },
    \"destination\": { \"iban\": \"CR07058919882707497149\" },
    \"parameters\": {
      \"origin_currency\": \"USD\",
      \"destination_currency\": \"CRC\",
      \"origin_amount\": 1000,
      \"quote_id\": \"$QUOTE_ID\",
      \"description\": \"Pago USD a CRC\"
    }
  }"
# { "reference_code": "FXT-20260428-123456", "status": "PENDING_NEW", ... }

Ejemplos multi-lenguaje

Las dos operaciones anteriores, generadas desde el spec OpenAPI vivo. Elija su lenguaje; su elección persiste a través de otras páginas.

`getQuote`

curl -fsS -X POST "https://sandbox-api.ariari.xyz/api/v1/fxorder/getQuote" \
  -H "Ocp-Apim-Subscription-Key: $YOUR_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "customer_external_user_id": "550e8400-e29b-41d4-a716-446655440000",
  "destination_amount": 50000,
  "destination_currency": "CRC",
  "lock_rate": true,
  "origin_amount": 1000,
  "origin_currency": "USD"
}'

`createOrder`

curl -fsS -X POST "https://sandbox-api.ariari.xyz/api/v1/fxorder/createOrder" \
  -H "Ocp-Apim-Subscription-Key: $YOUR_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "destination": {
    "external_account_id": "ext_550e8400-e29b-41d4-a716-446655440002",
    "iban": "CR05015202001026284060",
    "iban_holder": "Comercial La Sabana",
    "wallet_id": "550e8400-e29b-41d4-a716-446655440001"
  },
  "origin": {
    "external_account_id": "ext_550e8400-e29b-41d4-a716-446655440000",
    "wallet_id": "550e8400-e29b-41d4-a716-446655440000"
  },
  "parameters": {
    "description": "FX conversion USD to CRC",
    "destination_amount": 50000,
    "destination_currency": "CRC",
    "origin_amount": 1000,
    "origin_currency": "USD",
    "quote_id": "fxq_a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }
}'

`getStatus`

curl -fsS -X POST "https://sandbox-api.ariari.xyz/api/v1/fxorder/getStatus" \
  -H "Ocp-Apim-Subscription-Key: $YOUR_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "reference_code": "FXT-20260202-123456"
}'

La forma XOR de monto exclusivo

Tanto getQuote como createOrder usan la misma convención de anclaje de monto. Establezca exactamente uno de origin_amount o destination_amount — nunca ambos, nunca ninguno (excepto para cotizaciones de solo-tasa; vea abajo).

Forma Significado El servidor calcula
origin_amount definido, destination_amount ausente "Tengo X de moneda origen — ¿cuánto destino recibo?" destination = FLOOR((origin - fee) × rate)
destination_amount definido, origin_amount ausente "Quiero exactamente X de moneda destino — ¿cuánto origen debo enviar?" origin = CEILING(destination / rate + fee)
Ambos ausentes (solo en getQuote) "Solo deme la tasa actual, sin monto específico" Retorna snapshot de tasa solamente — sin origin_amount/destination_amount/fee_amount
Ambos presentes Inválido — retorna 400 con error_code: DB-FX-410

El redondeo va a favor del cliente en ambas direcciones: FLOOR en la cotización anclada al destino asegura que usted no envíe más de lo que el socio pidió; CEILING en la derivación del origen asegura que no se sub-cobre en una orden anclada al destino.


getQuote — solicitud

{
  "origin_currency": "USD",
  "destination_currency": "CRC",
  "origin_amount": 1000,
  "lock_rate": true,
  "customer_external_user_id": "550e8400-e29b-41d4-a716-446655440000"
}
Campo Tipo Requerido Descripción
origin_currency string (ISO 4217) Moneda que está enviando.
destination_currency string (ISO 4217) Moneda que está recibiendo. Debe diferir de origin_currency.
origin_amount number XOR Monto en moneda origen. Mutuamente exclusivo con destination_amount.
destination_amount number XOR Monto en moneda destino. Mutuamente exclusivo con origin_amount.
lock_rate boolean no (predeterminado false) Si es true, el servidor retorna un quote_id válido por 60 segundos.
customer_external_user_id string (UUID) no Solo M2M — el socio está solicitando esta cotización en nombre de un cliente específico. La respuesta usa el nivel de stream de ese cliente. Cuando se omite, ARi cotiza al nivel predeterminado del socio.

customer_external_user_id lleva al cliente para quien el socio está cotizando — no es la identidad propia del socio. La identidad del socio se deriva del mapeo de la suscripción de APIM; nunca pase una identidad de socio aquí.


getQuote — respuesta

{
  "quote_id": "fxq_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "origin_currency": "USD",
  "destination_currency": "CRC",
  "rate": "512.750000",
  "bid_rate": "510.250000",
  "ask_rate": "515.250000",
  "quoted_at": "2026-04-28T10:30:00.000Z",
  "expires_at": "2026-04-28T10:31:00.000Z",
  "valid_for_seconds": 60,
  "origin_amount": 1000,
  "destination_amount": 512750,
  "fee_amount": 2.5,
  "fee_currency": "USD"
}
Campo Tipo Cuándo presente Notas
quote_id string solo cuando lock_rate=true Formato: fxq_{uuid}. Un solo uso; pase a createOrder para anclar la tasa.
origin_currency string siempre Refleja la solicitud.
destination_currency string siempre Refleja la solicitud.
rate string siempre Tasa final mezclada por nivel a 6 decimales de precisión. Use directamente — su nivel ya está incorporado al precio.
bid_rate / ask_rate string cuando se suministró un lado de monto Bid/ask final mezclado por nivel. Úselos para previsualizar la pierna inversa (p. ej. CRC→USD cuando la cotización bloqueada es USD→CRC) sin emitir una nueva getQuote.
quoted_at string (ISO 8601) siempre Cuándo ARi calculó la tasa.
expires_at string (ISO 8601) solo cuando lock_rate=true Corte del lado del servidor para usar quote_id en createOrder.
valid_for_seconds integer siempre Conveniencia — (expires_at - quoted_at) en segundos enteros (60 hoy).
origin_amount number cuando se suministró un lado de monto Reflejo cuando es anclado al origen; calculado cuando es anclado al destino.
destination_amount number cuando se suministró un lado de monto Reflejo cuando es anclado al destino; calculado cuando es anclado al origen.
fee_amount number cuando se suministró un lado de monto Comisión calculada por ARi en el lado del origen; nunca declarada por el socio.
fee_currency string cuando fee_amount está presente Siempre igual a origin_currency hoy.

getQuote — tres escenarios

Anclado al origen ("Tengo 1000 USD")

curl -fsS -X POST "https://sandbox-api.ariari.xyz/api/v1/fxorder/getQuote" \
  -H "Ocp-Apim-Subscription-Key: $SU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "origin_currency": "USD",
    "destination_currency": "CRC",
    "origin_amount": 1000,
    "lock_rate": true
  }'

La respuesta incluye origin_amount: 1000 (reflejado) y destination_amount: 512750.00 (calculado usando el bid_rate bloqueado, con redondeo FLOOR para que el cliente nunca reciba menos de lo que la matemática sugeriría por una fracción de centavo).

Anclado al destino ("Quiero exactamente 500000 CRC")

curl -fsS -X POST "https://sandbox-api.ariari.xyz/api/v1/fxorder/getQuote" \
  -H "Ocp-Apim-Subscription-Key: $SU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "origin_currency": "USD",
    "destination_currency": "CRC",
    "destination_amount": 500000,
    "lock_rate": true
  }'

La respuesta incluye destination_amount: 500000.00 (reflejado) y origin_amount: 983.16 (calculado usando el ask_rate bloqueado, con redondeo CEILING para que el cliente siempre envíe lo suficiente para cubrir lo que pidió recibir).

Bloqueo de solo tasa (ambos montos omitidos)

curl -fsS -X POST "https://sandbox-api.ariari.xyz/api/v1/fxorder/getQuote" \
  -H "Ocp-Apim-Subscription-Key: $SU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "origin_currency": "USD",
    "destination_currency": "CRC",
    "lock_rate": true
  }'

La respuesta es un snapshot de tasa — quote_id, rate, bid_rate, ask_rate, quoted_at, expires_at — y sin origin_amount/destination_amount/fee_amount. Útil para mostrar tasas en vivo en una UI antes de que el usuario elija un lado.

El quote_id de un bloqueo de solo-tasa sigue siendo de un solo uso y consumible en createOrder — suministre su lado XOR de monto al momento de la creación de la orden.


createOrder — solicitud

{
  "origin": {
    "wallet_id": "550e8400-e29b-41d4-a716-446655440000"
  },
  "destination": {
    "iban": "CR07058919882707497149",
    "iban_holder": "Mario Herrera"
  },
  "parameters": {
    "origin_currency": "USD",
    "destination_currency": "CRC",
    "origin_amount": 1000,
    "quote_id": "fxq_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "description": "Pago USD a CRC"
  }
}

`origin` — exactamente uno de:

Campo Tipo Caso de uso
wallet_id UUID Una wallet ARi de su propiedad.
external_account_id UUID Una cuenta bancaria externa pre-registrada y verificada.

`destination` — exactamente uno de:

Campo Tipo Caso de uso
wallet_id UUID Crédito interno a una wallet ARi.
external_account_id UUID Una cuenta externa pre-registrada y verificada.
iban string IBAN ad-hoc para pagos de una sola vez. Empareje con iban_holder.

Cuando usa iban, iban_holder es requerido. Cuando usa un ID de cuenta registrada, iban_holder se ignora.

`parameters`

Campo Tipo Requerido Notas
origin_currency string (ISO 4217) Debe diferir de destination_currency.
destination_currency string (ISO 4217) Debe diferir de origin_currency.
origin_amount number XOR Mutuamente exclusivo con destination_amount.
destination_amount number XOR Mutuamente exclusivo con origin_amount.
quote_id string no Si se suministra, la orden se ancla a la tasa bloqueada de esa cotización. No debe estar expirada (expires_at) ni ya consumida.
description string no Texto libre, mostrado en la línea de tiempo de la orden. ≤ 500 caracteres.

Si suministra tanto un quote_id como un origin_amount/destination_amount explícito, el lado de monto suministrado debe coincidir con el anclaje de la cotización. Cruzar lados retorna 400 con error_code: DB-FX-410.

Si omite quote_id, la orden se ejecuta a la tasa de mercado actual al momento de creación. La tasa registrada en la respuesta (campo rate) es la tasa efectivamente aplicada — nunca la tasa al momento de algún getQuote anterior.


createOrder — respuesta

{
  "reference_code": "FXT-20260428-123456",
  "status": "PENDING_NEW",
  "origin_currency": "USD",
  "destination_currency": "CRC",
  "amount": "1000.00",
  "rate": "512.750000",
  "converted_amount": "512750.00",
  "quote_id": "fxq_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "created_at": "2026-04-28T10:30:05.000Z"
}
Campo Descripción
reference_code Identificador hacia el socio en formato FXT-AAAAMMDD-NNNNNN. Páselo a getStatus.
status Estado inicial — usualmente PENDING_NEW, avanza asíncronamente. Vea ciclo de vida del estado.
amount Monto del lado origen, como string con 2 decimales de precisión. Siempre presente (post-resolución del anclaje).
rate Tasa final mezclada aplicada. null hasta la primera transición más allá de PENDING_NEW.
converted_amount Monto del lado destino post-aplicación-de-tasa.
quote_id Reflejado solo si suministró uno.
created_at ISO 8601, UTC.

El código de referencia es el único identificador que debe almacenar. Los GUIDs internos de orden de ARi están intencionalmente no expuestos.


Ciclo de vida del estado

PENDING_NEW ──> NEW ──> ACCEPTED ──> FILLED
     │           │          │
     │           │          └──> REJECTED   (falla de regla durante ejecución)
     │           │
     │           └──> CANCELED   (iniciado por operador, antes de ejecución)
     │
     └──> REJECTED   (falla de validación síncrona)
Estado ¿Terminal? Significado
PENDING_NEW no Aceptada por ARi, esperando validación inicial.
NEW no Validada, en cola para ejecución.
ACCEPTED no Ejecución en progreso en el proveedor de tasas.
FILLED Exitosa — el dinero se está moviendo / se ha movido.
REJECTED Fallida — vea error_code en la respuesta de getStatus.
CANCELED Cancelada por operador antes de ejecución.

Use Webhooks (eventos fx.rate_changed, completación de transferencias) para recibir transiciones en tiempo real en lugar de sondear.


getStatus

curl -fsS -X POST "https://sandbox-api.ariari.xyz/api/v1/fxorder/getStatus" \
  -H "Ocp-Apim-Subscription-Key: $SU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{ "reference_code": "FXT-20260428-123456" }'
{
  "reference_code": "FXT-20260428-123456",
  "status": "FILLED",
  "description": "Pago USD a CRC",
  "last_updated_at": "2026-04-28T10:31:42.000Z",
  "origin": {
    "currency": "USD",
    "amount": "1000.00",
    "wallet_id": "550e8400-e29b-41d4-a716-446655440000"
  },
  "destination": {
    "currency": "CRC",
    "amount": "512750.00",
    "iban": "CR07058919882707497149"
  },
  "rate": "512.750000",
  "fee_amount": 2.5,
  "fee_currency": "USD"
}

La forma es anidada en {origin, destination} en lugar de plana — cada lado lleva su propia moneda, monto, e identificador. last_updated_at es el timestamp de la transición de estado más reciente.

Para estado terminal REJECTED, la respuesta incluye un error_code (uno de los valores DB-FX-* de Errores) que nombra la falla precisa.


getHistory

POST con cuerpo paginado — vea Paginación para el contrato page / pageSize y los filtros de fecha.

curl -fsS -X POST "https://sandbox-api.ariari.xyz/api/v1/fxorder/getHistory" \
  -H "Ocp-Apim-Subscription-Key: $SU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "page": 1,
    "pageSize": 50,
    "start_date": "2026-04-01",
    "end_date": "2026-04-30",
    "status": "FILLED"
  }'

El filtro status es opcional. Omítalo para recibir todos los estados de la ventana.


Encabezados requeridos

Encabezado Requerido Notas
Ocp-Apim-Subscription-Key Su clave de APIM. Vea Autenticación.
Idempotency-Key sí en createOrder UUID v4 por orden lógica; seguro reintentar con la misma clave. Vea Idempotencia.
Content-Type application/json.
X-Correlation-Id no (recomendado) UUID — propáguelo si llama a ARi mientras maneja un webhook del mismo flujo.

Pares de monedas soportados

De A
USD CRC
CRC USD
USD EUR
EUR USD
EUR CRC
CRC EUR

Pares no en esta lista retornan 400 con error_code: DB-FX-403 (par no soportado).


Errores comunes

El catálogo completo está en Errores; las entradas específicas de FX más frecuentes:

error_code HTTP Causa
DB-FX-410 422 Violación XOR en el lado de monto (ambos suministrados o ambos omitidos en createOrder).
DB-FX-411 410 quote_id expirado. Solicite una cotización nueva.
DB-FX-412 409 quote_id ya consumido por un createOrder previo.
DB-FX-413 400 quote_id no encontrado o malformado.
DB-FX-414 400 Discrepancia de moneda entre quote_id y cuerpo de solicitud.
DB-FX-415 422 Discrepancia de lado de monto entre quote_id y solicitud (cotización anclada al origen, orden anclada al destino).
DB-FX-417 422 Monto bajo el mínimo por moneda o sobre el máximo por nivel.
GW-STC-007 422 KYC bloqueado para el cliente con alcance vía customer_external_user_id.

Todos los errores siguen RFC 7807application/problem+json con error_code estable. Siempre registre trace_id de la respuesta al contactar soporte.


Uso del sandbox

El sandbox replica la misma forma del cable con sufijos de IBAN determinísticos — 0000 para FILLED, 0001 para REJECTED con saldo insuficiente, 0002 para escenarios de cotización-expirada, etc. Vea Sandbox para la matriz completa de IBANs determinísticos y la cadencia de recarga del pool de semillas.


Vea también

  • Referencia del objeto `FxQuote` — esquema en el cable y garantías campo por campo de las respuestas de getQuote
  • Referencia del objeto `FxOrder` — esquema y ciclo de vida de createOrder / getStatus / getHistory
  • Autenticación — modelo de clave de suscripción APIM, scopes por endpoint
  • Idempotencia — ciclo de vida de Idempotency-Key, comportamiento de reintentos, semántica de conflictos
  • Paginación — forma de consulta de getHistory
  • Errores — catálogo completo de error_code
  • Webhooks — reciba eventos fx.rate_changed y de completación de transferencias
  • Sandbox — IBANs de prueba determinísticos e identidades de prueba de socio
  • Registro de Cambios — evolución de la forma del cable y cronograma de deprecación