FITALAPI

Crear un cobro

POST /v1/collections

Crea un cobro y devuelve la instrucción para quien paga. Exige Idempotency-Key y firma. Envía amount o destination_amount. 201 si quedó creado; 202 si aún no se confirma: repite con la misma clave.

DatoValor
Método y rutaPOST /v1/collections
Servidorhttps://widget.fital.xyz
operationIdcreateCollection
AccesoLlave con el scope collections:write
Firma de peticiónObligatoria en producción y en la cuenta de prueba. El simulador público no la valida. Ver Firma de petición
IdempotenciaObligatoria, con Idempotency-Key. Ver Idempotencia
  • Envía amount, lo que paga tu cliente, o destination_amount, lo que quieres recibir. Al menos uno.
  • payer lleva los datos de quien paga, y se comprueba contra payer_fields de GET /v1/collection-methods. countrySymbol, en 2 letras, sólo si quien paga es de otro país que la moneda: si no, se deduce de la moneda.
  • 202 significa que el alta aún no se confirma: instruction y expires_at llegan en null. Repite con la misma clave, o consulta el id.

Encabezados de la petición

EncabezadoObligatorioDescripción
AuthorizationSíBearer y tu llave.
Content-TypeSíapplication/json.
Idempotency-KeySíClave única de esta operación, de 8 a 200 caracteres. Repetirla con el mismo cuerpo devuelve la misma respuesta durante 7 días. Con otro cuerpo da 422 idempotency_key_reused, también pasado ese plazo.
X-Fital-TimestampSíHora Unix en segundos. Se acepta hasta 300 s de diferencia.
X-Fital-SignatureSíFirma v1= de la petición con el secreto de firma de la llave.
X-Fital-ScenarioNoSólo simuladores: elige la respuesta que quieres practicar.

Cuerpo

CampoTipoObligatorioDescripción
amounttextoNoMonto que paga tu cliente. Envía éste o destination_amount.
currencytextoSíMoneda del cobro.
descriptiontextoNoConcepto del cobro, hasta 140 caracteres imprimibles. Hasta 140 caracteres.
destination_amounttextoNoMonto que quieres recibir. Envía éste o amount.
expires_attextoNoVencimiento deseado. Tiene que ser futuro en un cobro efectivo, que sin este campo vence a los 3 días. ISO 8601 en UTC.
external_reftextoNoTu referencia del cobro. Vuelve en el cobro y en sus eventos. Hasta 120 caracteres.
metadataobjetoNoHasta 20 pares clave y valor tuyos.
methodtextoSíMétodo de GET /v1/collection-methods. efectivo, disponible bajo solicitud a FITAL, cobra en tiendas de México, en MXN: se paga con una referencia y se envía amount, no destination_amount. Hasta 40 caracteres.
payerobjetoNoDatos de quien paga. payer_fields de GET /v1/collection-methods dice qué pide cada método: sin un campo required, o con un valor fuera de options, la respuesta es 400 invalid_request con el campo en details.fields, antes de crear nada. countrySymbol, en 2 letras, sólo si quien paga es de otro país que la moneda.
quote_idtextoNoCotización previa del cobro, si la tienes. Hasta 100 caracteres.
{
  "currency": "MXN",
  "method": "spei",
  "amount": "1250.00",
  "payer": {
    "firstName": "CLIENTE",
    "lastName": "FICTICIO",
    "email": "pagos@empresa-ejemplo.com"
  },
  "description": "Pedido 7731",
  "external_ref": "pedido-7731"
}

Respuestas correctas

201 Creado

CampoTipoObligatorioDescripción
amounttexto o nullSíMonto que paga tu cliente.
created_attexto o nullSíFecha de creación. ISO 8601 en UTC.
currencytexto o nullSíMoneda del cobro.
destination_amounttexto o nullSíMonto que recibes.
expires_attexto o nullSíVencimiento del cobro. ISO 8601 en UTC.
external_reftexto o nullSíTu referencia.
idtextoSíIdentificador del cobro, con prefijo col_.
instructionobjeto o nullSíCómo pagar. null mientras no esté lista.
instruction.typetexto o nullSíTipo de instrucción. Depende del método: trata la lista como extensible. LINK: value es una dirección que se abre para pagar. QR: value se muestra o se copia, como un QR o un texto de pago. REFERENCE: value es la referencia que quien paga da en la tienda, en un cobro efectivo. clabe: value es una CLABE. Sólo en sandbox.
instruction.valuetexto o nullSíDato que usa quien paga.
metadataobjetoSíLos pares que enviaste al crear el cobro.
methodtexto o nullSíMétodo del cobro.
statustextoSíEstado del cobro. processing: El alta aún no se confirma. pending: Espera el pago. paid: Pagado. No vuelve atrás. En efectivo, la tienda ya confirmó el pago y el abono a tu cuenta llega después. expired: Venció sin pago. No vuelve atrás. failed: No se pudo crear o completar.

Valores de instruction.type

ValorSignificado
LINKvalue es una dirección que se abre para pagar.
QRvalue se muestra o se copia, como un QR o un texto de pago.
REFERENCEvalue es la referencia que quien paga da en la tienda, en un cobro efectivo.
clabevalue es una CLABE. Sólo en la cuenta de prueba.

Depende del método. Trata la lista como extensible.

Valores de status

ValorSignificado
processingEl alta aún no se confirma.
pendingEspera el pago.
paidPagado. No vuelve atrás.
expiredVenció sin pago. No vuelve atrás.
failedNo se pudo crear o completar.
{
  "amount": "1250.00",
  "created_at": "2026-11-04T18:00:00.000Z",
  "currency": "MXN",
  "destination_amount": null,
  "expires_at": "2026-11-05T18:00:00.000Z",
  "external_ref": "pedido-7731",
  "id": "col_4hQvN8sLd1ZxK6tB2mYpR7cW",
  "instruction": {
    "type": "clabe",
    "value": "999180000000000022"
  },
  "metadata": {},
  "method": "spei",
  "status": "pending"
}

Instrucción clabe: sólo en sandbox.:

{
  "amount": "1250.00",
  "created_at": "2026-11-04T18:00:00.000Z",
  "currency": "MXN",
  "destination_amount": null,
  "expires_at": "2026-11-05T18:00:00.000Z",
  "external_ref": "pedido-7731",
  "id": "col_4hQvN8sLd1ZxK6tB2mYpR7cW",
  "instruction": {
    "type": "clabe",
    "value": "999180000000000022"
  },
  "metadata": {},
  "method": "spei",
  "status": "pending"
}

Instrucción REFERENCE: quien paga da value en la tienda.:

{
  "amount": "1250.00",
  "created_at": "2026-11-04T18:00:00.000Z",
  "currency": "MXN",
  "destination_amount": null,
  "expires_at": "2026-11-05T18:00:00.000Z",
  "external_ref": "pedido-7731",
  "id": "col_4hQvN8sLd1ZxK6tB2mYpR7cW",
  "instruction": {
    "type": "REFERENCE",
    "value": "999000000000000000000000000022"
  },
  "metadata": {},
  "method": "efectivo",
  "status": "pending"
}

Instrucción LINK: value se abre para pagar:

{
  "amount": "1250.00",
  "created_at": "2026-11-04T18:00:00.000Z",
  "currency": "MXN",
  "destination_amount": null,
  "expires_at": "2026-11-05T18:00:00.000Z",
  "external_ref": "pedido-7731",
  "id": "col_4hQvN8sLd1ZxK6tB2mYpR7cW",
  "instruction": {
    "type": "LINK",
    "value": "https://empresa-ejemplo.com/pagar/4hQvN8sL"
  },
  "metadata": {},
  "method": "spei",
  "status": "pending"
}

Instrucción QR: value se muestra o se copia:

{
  "amount": "1250.00",
  "created_at": "2026-11-04T18:00:00.000Z",
  "currency": "MXN",
  "destination_amount": null,
  "expires_at": "2026-11-05T18:00:00.000Z",
  "external_ref": "pedido-7731",
  "id": "col_4hQvN8sLd1ZxK6tB2mYpR7cW",
  "instruction": {
    "type": "QR",
    "value": "00020101021226580014ejemplo0212FICTICIO12345204000053034845802MX"
  },
  "metadata": {},
  "method": "spei",
  "status": "pending"
}

202 Aceptado: el resultado final llega después

Misma forma que la respuesta anterior.

{
  "amount": "1250.00",
  "created_at": "2026-11-04T18:00:00.000Z",
  "currency": "MXN",
  "destination_amount": null,
  "expires_at": null,
  "external_ref": "pedido-7731",
  "id": "col_4hQvN8sLd1ZxK6tB2mYpR7cW",
  "instruction": null,
  "metadata": {},
  "method": "spei",
  "status": "processing"
}

Encabezados de la respuesta

EncabezadoContenido
X-Request-IdIdentificador de la petición. Cítalo con soporte.
Idempotent-Replayedtrue si la respuesta repite una anterior con esa clave.
LocationRuta del recurso, con 201 y 202.
Retry-AfterSegundos a esperar, con 429, con 409 idempotency_in_progress y a veces con 503.

Errores

Todos usan la forma de Errores: error.code, message, request_id, details, retryable y docs_url. Decide por code.

HTTPCódigoCuándo saleretryable
400invalid_requestFalta amount o destination_amount, o algún campo no cumple el esquema. También sin un campo required de payer_fields, o con un valor fuera de options: details.fields lo nombra como payer.<campo>.false
400idempotency_key_requiredFalta Idempotency-Key.false
400idempotency_key_invalidLa clave no mide entre 8 y 200 caracteres, o viene duplicada.false
401invalid_api_keyLa llave falta, está mal formada, venció o fue revocada, o la empresa está suspendida.false
401wrong_environmentLa llave es de otro ambiente.false
401invalid_signatureFalta la firma, no coincide o tiene más de 5 minutos.false
403account_restrictedLa cuenta tiene una restricción.false
403not_enabledEl método no está activado para tu empresa.false
403forbidden_scopeLa llave no tiene el scope de esta operación. details.required_scope lo nombra.false
403ip_not_allowedLa IP de origen no está activa en la lista de esa llave.false
403kyb_requiredLa empresa no tiene KYB aprobado o aún no está inscrita.false
403account_not_activeLa cuenta no está activa.false
409collection_not_readyEl cobro aún no se puede crear porque falta una revisión de tu cuenta.false
409idempotency_in_progressUna petición anterior con esa clave sigue en curso. Llega con Retry-After.true
413invalid_requestEl cuerpo supera 64 KB.false
422rail_unavailableEl país del método no está disponible por ahora.true
422in_reviewUna revisión de tu cuenta frena la operación.true
422method_unavailableEl método no está disponible.false
422amount_out_of_rangeEl monto está fuera del rango del método.false
422collection_rejectedEl cobro se rechazó.false
422idempotency_key_reusedUsaste esa clave con otro cuerpo, también pasados 7 días.false
429rate_limitedSuperaste un límite de peticiones. Llega con Retry-After.true
429sandbox_limit (sólo cuenta de prueba)Alcanzaste 100 pagos y cobros de hoy.false
500internal_errorFallo nuestro. El resultado de la petición no se conoce.true
503service_unavailableEl servicio no está disponible por ahora. El resultado de la petición no se conoce.true

Ejemplos de error

400 invalid_request:

{
  "error": {
    "code": "invalid_request",
    "message": "Petición inválida",
    "request_id": "0123456789abcdef0123456789abcdef",
    "details": {
      "fields": {
        "amount": "Valor inválido"
      }
    },
    "retryable": false,
    "docs_url": "https://merchants.fital.xyz/docs/errores/#invalid_request"
  }
}

401 invalid_api_key:

{
  "error": {
    "code": "invalid_api_key",
    "message": "Credencial inválida",
    "request_id": "0123456789abcdef0123456789abcdef",
    "details": {},
    "retryable": false,
    "docs_url": "https://merchants.fital.xyz/docs/errores/#invalid_api_key"
  }
}

403 account_restricted:

{
  "error": {
    "code": "account_restricted",
    "message": "Tu cuenta tiene una restricción. Contacta a tu ejecutivo FITAL.",
    "request_id": "0123456789abcdef0123456789abcdef",
    "details": {},
    "retryable": false,
    "docs_url": "https://merchants.fital.xyz/docs/errores/#account_restricted"
  }
}

409 collection_not_ready:

{
  "error": {
    "code": "collection_not_ready",
    "message": "Operación rechazada",
    "request_id": "0123456789abcdef0123456789abcdef",
    "details": {},
    "retryable": false,
    "docs_url": "https://merchants.fital.xyz/docs/errores/#collection_not_ready"
  }
}

413 invalid_request:

{
  "error": {
    "code": "invalid_request",
    "message": "Petición inválida",
    "request_id": "0123456789abcdef0123456789abcdef",
    "details": {},
    "retryable": false,
    "docs_url": "https://merchants.fital.xyz/docs/errores/#invalid_request"
  }
}

422 rail_unavailable:

{
  "error": {
    "code": "rail_unavailable",
    "message": "Operación rechazada",
    "request_id": "0123456789abcdef0123456789abcdef",
    "details": {},
    "retryable": true,
    "docs_url": "https://merchants.fital.xyz/docs/errores/#rail_unavailable"
  }
}

429 rate_limited:

{
  "error": {
    "code": "rate_limited",
    "message": "Demasiadas peticiones",
    "request_id": "0123456789abcdef0123456789abcdef",
    "details": {},
    "retryable": true,
    "docs_url": "https://merchants.fital.xyz/docs/errores/#rate_limited"
  }
}

500 internal_error:

{
  "error": {
    "code": "internal_error",
    "message": "Error interno",
    "request_id": "0123456789abcdef0123456789abcdef",
    "details": {},
    "retryable": true,
    "docs_url": "https://merchants.fital.xyz/docs/errores/#internal_error"
  }
}

503 service_unavailable:

{
  "error": {
    "code": "service_unavailable",
    "message": "Servicio no disponible",
    "request_id": "0123456789abcdef0123456789abcdef",
    "details": {},
    "retryable": true,
    "docs_url": "https://merchants.fital.xyz/docs/errores/#service_unavailable"
  }
}

Reintentos

Ante un timeout, repite con la misma clave. Un 400 por payer no crea nada, pero la clave conserva ese cuerpo: el cuerpo corregido necesita otra Idempotency-Key. Con la misma responde 422 idempotency_key_reused.

  • 409 idempotency_in_progress: Espera Retry-After y repite con la misma clave.
  • 422 rail_unavailable: Repite más tarde.
  • 422 in_review: Mira retryable. Con true, repite con la misma clave cuando la revisión se levante. Con false, el rechazo quedó registrado y esa clave lo repite: usa una clave nueva tras la revisión.
  • 429 rate_limited: Espera lo que indique Retry-After y repite.
  • 500 internal_error: Repite con espera exponencial.
  • 503 service_unavailable: Repite con espera exponencial. Respeta Retry-After si viene.

Ver Reintentos y Límites.

Muestras

Variables de estas muestras: simulador público, sin cuenta

Define esto en tu terminal. Con estos valores las muestras corren tal cual contra el simulador público:

export FITAL_API="https://widget.fital.xyz/explorar/v1"
export FITAL_KEY="sk_test_sandbox_publica"
export FITAL_SIGNING_SECRET="secreto_de_firma_de_prueba_publico"

En la cuenta de prueba o en producción cambia la base, la llave y el secreto, y sustituye los id de ejemplo por los tuyos. Ver Ambientes.

op-crear-cobro.sh
CUERPO='{"currency":"MXN","method":"spei","amount":"1250.00","payer":{"firstName":"CLIENTE","lastName":"FICTICIO","email":"pagos@empresa-ejemplo.com"},"description":"Pedido 7731","external_ref":"pedido-7731"}'
CLAVE="ejemplo-crear-cobro"
TS=$(date +%s)
HASH=$(printf '%s' "$CUERPO" | openssl dgst -sha256 -r | cut -d' ' -f1)
FIRMA=$(printf '%s\n%s\n%s\n%s\n%s' "$TS" POST /v1/collections "$HASH" "$CLAVE" \
  | openssl dgst -sha256 -hmac "$FITAL_SIGNING_SECRET" -r | cut -d' ' -f1)
curl -sS -X POST "$FITAL_API/collections" \
  -H "Authorization: Bearer $FITAL_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $CLAVE" \
  -H "X-Fital-Timestamp: $TS" \
  -H "X-Fital-Signature: v1=$FIRMA" \
  -d "$CUERPO"

Guías

Ver esta página en Markdown

En esta página