Convenciones de la API
URL base y versión
Sección titulada «URL base y versión»https://sag.efipay.co/api/v1La versión va en la ruta. Hoy solo existe v1; cuando exista otra, v1 seguirá
respondiendo.
Dentro de una versión solo agregamos: campos nuevos en las respuestas, parámetros opcionales nuevos, valores nuevos en las enumeraciones. Nunca quitamos un campo ni cambiamos el significado de uno existente.
Formato de las peticiones
Sección titulada «Formato de las peticiones»- Cuerpo en JSON, con
Content-Type: application/json. - Header
Accept: application/json. Sin él, un error de validación puede llegarte como HTML en lugar de JSON. - Autenticación con
Authorization: Bearer. Ver Autenticación. - Nombres de campo en
snake_caseen lo que envías.
La única excepción es crear una sucursal con
logo o RUT, que va como multipart/form-data porque lleva archivos.
Paginación
Sección titulada «Paginación»Los listados vienen paginados con el sobre estándar de Laravel:
{ "data": [ "..." ], "links": { "first": "https://sag.efipay.co/api/v1/...?page=1", "last": "https://sag.efipay.co/api/v1/...?page=8", "prev": null, "next": "https://sag.efipay.co/api/v1/...?page=2" }, "meta": { "current_page": 1, "from": 1, "last_page": 8, "per_page": 15, "to": 15, "total": 118 }}| Parámetro | Qué hace | Por defecto |
|---|---|---|
| page | Página a traer | 1 |
| per_page | Cuántos elementos por página | 15 en la mayoría de los endpoints |
Para recorrer todo, sigue links.next hasta que sea null. No calcules las URLs a
mano.
Fechas y horas
Sección titulada «Fechas y horas»| Contexto | Formato | Ejemplo |
|---|---|---|
| Filtros de fecha que envías | Y-m-d |
2026-07-31 |
| Vencimiento de tarjeta | Y-m |
2030-12 |
| Fecha tope de un plan | Y-m-d H:i:s |
2026-12-31 23:59:59 |
Fechas que devolvemos (created_at, approved_at…) |
ISO 8601 en UTC | 2026-07-31T14:32:10.000000Z |
| Fechas en los webhooks | Hora de Colombia | 2026-07-31 09:32:10 |
Fechas legadas de suscripciones (ends_at, canceled_at…) |
Y-m-d H:i:s, hora de Colombia |
2026-10-28 11:28:00 |
Día de cobro de suscripciones (next_transaction, next_renew_at) |
Y-m-d, día en Colombia |
2026-10-28 |
Instantes nuevos de suscripciones (ends_at_iso, next_charge_at, charge_scheduled_at, cancel_at) |
ISO 8601 con offset explícito | 2026-10-28T19:00:00-05:00 |
Errores
Sección titulada «Errores»Todo error de la API v1 (4xx) trae, además de message (y errors en un 422), un
objeto error con la misma forma:
{ "message": "Este cobro ya tiene una transacción y no permite reintentos", "error": { "type": "invalid_request_error", "code": "payment_already_used", "message": "Este cobro ya tiene una transacción y no permite reintentos", "param": "payment.id" }}Decide con error.code, que es estable; el texto de message puede cambiar. El
catálogo de tipos y códigos está en Códigos de error.
Metadata
Sección titulada «Metadata»Suscripciones, suscriptores,
planes y cobros generados aceptan un
objeto metadata de pares clave-valor para guardar tus propias referencias, al estilo
de Stripe: hasta 50 claves de 1 a 40 caracteres ([A-Za-z0-9_-]) y valores de texto o
número de hasta 500 caracteres, que se guardan como texto. Te lo devolvemos en las
consultas y en los webhooks.
Montos y monedas
Sección titulada «Montos y monedas»Los montos van como número decimal en la unidad principal de la moneda, no en
centavos: 120000 son ciento veinte mil pesos, y 1200.50 son mil doscientos pesos con
cincuenta centavos.
| Moneda | Código | Notas |
|---|---|---|
| Peso colombiano | COP |
La moneda de operación. Máximo 999.999.999.999 |
| Dólar | USD |
Se convierte a COP con la TRM del día. Máximo 200.000.000 |
| Euro | EUR |
Igual que USD |
En una transacción en moneda extranjera verás amount (moneda original), value_cop (el
equivalente en pesos) y currency_rate_conversion con la tasa aplicada. Concilia con
value_cop: es lo que efectivamente se movió.
Identificadores
Sección titulada «Identificadores»| Tipo | Aspecto | Dónde |
|---|---|---|
| UUID | 9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f |
Cobros, transacciones, planes, suscriptores, cupones, reservas, eventos de webhook (meta.event_id) |
| Consecutivo | 20481 |
transaction_id, el número que ve tu operador |
| Entero | 1 |
Sucursales |
Una transacción tiene los dos: id (UUID) y transaction_id (consecutivo). Los
endpoints de consulta aceptan cualquiera de los dos; guarda el que le vayas a mostrar a
una persona.
Idempotencia
Sección titulada «Idempotencia»Las escrituras de suscripciones, pagos y reservas de cupo aceptan el header
Idempotency-Key. Si repites la misma llamada con la misma clave y los mismos
parámetros, te devolvemos la respuesta original con el header
Idempotency-Replayed: true, sin volver a ejecutar el cobro.
| Rutas que la aceptan | |
|---|---|
/api/v1/subscriptions/* |
Crear, cancelar, cambiar de plan, actualizar la tarjeta, pausar, reanudar |
/api/v1/payment/generate-payment |
Generar el cobro |
/api/v1/payment/transaction-checkout/* |
Checkout por API: tarjeta, efectivo, PSE, Bre-B |
/api/v1/payment/refund-transaction |
Devoluciones |
/api/v1/mit/pre-authorizations/* |
Crear, autorizar, cobrar y liberar reservas |
curl -X POST \'/api/v1/subscriptions/subscription' \-H 'Authorization: Bearer ACCESS_TOKEN' \-H 'Content-type: application/json' \-H 'Idempotency-Key: 9b1f0e34-6b2a-4a7e-9d0a-uno-por-operacion' \-d '{ "...": "..." }'- Usa una clave distinta por operación (un UUID sirve). No reutilices la misma clave para dos cobros diferentes.
- La clave vale 24 horas y su alcance es tu comercio y el usuario del token.
- La misma clave con parámetros distintos responde
409. - La misma clave mientras la primera petición sigue en curso responde
409. - Los errores
5xxno se memorizan, para que puedas reintentar un fallo transitorio.
Detalle en Suscripciones.
Límites, tiempos y reintentos
Sección titulada «Límites, tiempos y reintentos»Límite de peticiones
Sección titulada «Límite de peticiones»No publicamos hoy un límite de peticiones por minuto. Lo que sí existe es un candado de concurrencia por cobro: no se procesan dos pagos del mismo cobro a la vez.
| Situación | Respuesta | Ventana |
|---|---|---|
| Otro pago del mismo cobro en curso (tarjeta, PSE, Bre-B) | 429, error.code: payment_in_progress |
10 segundos |
| Otro pago en efectivo del mismo cobro en curso | 429, error.code: payment_in_progress |
5 segundos |
Otra operación con la misma Idempotency-Key en curso |
409 |
Hasta que la primera termine |
Tiempos de respuesta
Sección titulada «Tiempos de respuesta»| Operación | Espera razonable | Nuestro tope contra la red |
|---|---|---|
| Catálogos y consultas | Menos de 1 s | — |
| Pago con tarjeta (sin 3DS) | 3 a 10 s | 45 s |
| Pago con tarjeta (con 3DS) | Depende del banco y del cliente | 45 s por llamada |
| Reserva de cupo | 3 a 10 s | 45 s |
Pon en tu cliente un timeout mayor a 45 segundos para las operaciones de cobro. Si cortas antes, no cancelas nada: la operación sigue su curso en la red y te quedas sin saber cómo terminó.
Qué es seguro reintentar
Sección titulada «Qué es seguro reintentar»| Respuesta | ¿Reintentar? |
|---|---|
5xx, error.retryable: true |
Sí, con espera creciente |
error.retryable: false (fondos, tarjeta vencida, fraude) |
No. El resultado será el mismo |
202, T01, o un timeout de tu lado |
No a ciegas. Estado indeterminado: consulta primero |
409 de idempotencia |
No: ya hay una operación con esa clave |
422 de validación |
Solo después de corregir la petición |
Lo que nunca devolvemos
Sección titulada «Lo que nunca devolvemos»- El número completo de una tarjeta. Siempre enmascarado:
491617******1313. - El CVV. No se guarda.
- Tokens en claro. El token de un cobro en modalidad
apiy el token de una tarjeta se devuelven una sola vez, al crearse. De ahí en adelante solo guardamos su hash.