{
  "openapi": "3.1.0",
  "info": {
    "title": "TuCapi — API de cobros y pagos, versión 2",
    "version": "2.8.0",
    "x-webhook-headers": [
      "X-Firma",
      "X-Timestamp",
      "X-Evento",
      "X-Id-Evento"
    ],
    "x-webhook-signature": {
      "header": "X-Firma",
      "version": "v1",
      "algorithm": "HMAC-SHA256",
      "signed": "<X-Timestamp> + \".\" + <cuerpo crudo>",
      "encoding": "hex",
      "clock_tolerance_seconds": 300,
      "rotation": "dos firmas separadas por espacio; alcanza con que una coincida"
    },
    "x-idempotency-header": "Idempotency-Key",
    "summary": "Cobros (pay-ins) y pagos (payouts) por país y moneda, con una sola integración.",
    "description": "Versión 2 de la API. Una sola integración que no cambia: país, moneda y método van en el cuerpo, y lo que se puede hacer hoy lo dice `GET /v2/capabilities`.\n\n## Cómo funciona un cobro con código\n\n1. `POST /v2/payins` con `method: debit_otp` → **201** con `status: pending` y `pending_reason: awaiting_code`. El banco del pagador le manda un código a su usuario.\n2. Su usuario teclea el código en la pantalla de usted.\n3. `POST /v2/payins/{id}/confirm` con `{\"code\": \"…\"}` → el débito se ejecuta. `confirmed` trae `bank_reference`; `failed` trae `failure.code`.\n\nSi el resultado no llegó en el momento, la operación queda `pending` / `processing`; consúltela con `GET /v2/payins/{id}`.\n\n## Las reglas que conviene entender antes de integrar\n\n**`Idempotency-Key` es obligatoria y es su red.** Reenviar el mismo pedido con la misma clave devuelve LA MISMA operación y no le pide otro código a su usuario. La misma clave con otro cuerpo es **409** `idempotency_key_reused`.\n\n**Los estados públicos son tres:** `pending`, `confirmed` y `failed`. Un `failed` siempre trae `failure.code`, un valor cerrado sobre el que puede hacer un `switch`.\n\n**Los errores llevan un solo sobre:** `{\"error\": {\"code\", \"message\", \"details\"}}`. Compare `code`, muestre `message`.\n\n## Autenticación\n\nCabecera `Authorization: Bearer <su llave>`: una llave `tuc_live_…` en producción, o `tuc_test_…` en el sandbox (`https://api.tucapi.app/sandbox`). La llave determina su empresa: no hay ningún campo que diga a nombre de quién opera."
  },
  "servers": [
    {
      "url": "https://api.tucapi.app",
      "description": "Producción."
    },
    {
      "url": "https://api.tucapi.app/sandbox",
      "description": "Sandbox: nada mueve dinero; sólo llaves tuc_test_…."
    }
  ],
  "security": [
    {
      "LlaveDeEmpresa": []
    }
  ],
  "tags": [
    {
      "name": "Catálogo",
      "description": "Lo que se puede hacer hoy: países, monedas, métodos, bancos y saldos."
    },
    {
      "name": "Cobros",
      "description": "Pay-ins: cobrarle a una persona."
    },
    {
      "name": "Pagos",
      "description": "Payouts: pagarle a una persona."
    },
    {
      "name": "Eventos",
      "description": "Lo que pasó con sus operaciones: por webhook y por consulta."
    },
    {
      "name": "Contrato",
      "description": "Este documento."
    }
  ],
  "paths": {
    "/v2/capabilities": {
      "get": {
        "operationId": "verCapacidades",
        "x-sdk": "capabilities.get",
        "tags": [
          "Catálogo"
        ],
        "summary": "Qué puede hacer su llave hoy",
        "description": "Países, monedas, métodos por dirección con sus campos, límites y disponibilidad, y `config_version`. Es la única fuente que necesita leer para saber qué puede hacer hoy.\n\nRequiere una llave válida, sin alcance particular.",
        "responses": {
          "200": {
            "description": "Las capacidades.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Capacidades"
                }
              }
            }
          },
          "401": {
            "description": "La llave falta o no es válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Falta la cabecera Authorization o la llave de API no es válida."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/methods": {
      "get": {
        "operationId": "listarMetodos",
        "x-sdk": "methods.list",
        "tags": [
          "Catálogo"
        ],
        "summary": "El catálogo de métodos",
        "description": "Filtrable por país y dirección. Requiere una llave válida, sin alcance particular.",
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z]{2}$"
            },
            "description": "ISO 3166-1 alfa-2. Sin él, todos los países."
          },
          {
            "name": "direction",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "payin",
                "payout"
              ]
            },
            "description": "Sin él, las dos direcciones."
          }
        ],
        "responses": {
          "200": {
            "description": "Los métodos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListaDeMetodos"
                }
              }
            }
          },
          "400": {
            "description": "Un parámetro inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "field_invalid": {
                    "value": {
                      "error": {
                        "code": "field_invalid",
                        "message": "Hay campos con un valor inválido."
                      }
                    }
                  },
                  "country_unsupported": {
                    "value": {
                      "error": {
                        "code": "country_unsupported",
                        "message": "El país no está disponible."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "La llave falta o no es válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Falta la cabecera Authorization o la llave de API no es válida."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/banks": {
      "get": {
        "operationId": "listarBancos",
        "x-sdk": "banks.list",
        "tags": [
          "Catálogo"
        ],
        "summary": "Los bancos de un país",
        "description": "Con `code`, `name` y qué métodos admite hoy cada uno. Requiere una llave válida, sin alcance particular.",
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z]{2}$"
            },
            "description": "ISO 3166-1 alfa-2."
          },
          {
            "name": "method",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/CodigoDeMetodo"
            },
            "description": "Sólo los bancos que admiten este método."
          }
        ],
        "responses": {
          "200": {
            "description": "Los bancos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListaDeBancos"
                }
              }
            }
          },
          "400": {
            "description": "Un parámetro inválido o faltante.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "field_required": {
                    "value": {
                      "error": {
                        "code": "field_required",
                        "message": "Faltan campos obligatorios."
                      }
                    }
                  },
                  "country_unsupported": {
                    "value": {
                      "error": {
                        "code": "country_unsupported",
                        "message": "El país no está disponible."
                      }
                    }
                  },
                  "method_unavailable": {
                    "value": {
                      "error": {
                        "code": "method_unavailable",
                        "message": "El método no está disponible para ese país y moneda."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "La llave falta o no es válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Falta la cabecera Authorization o la llave de API no es válida."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/balances": {
      "get": {
        "operationId": "verSaldos",
        "x-sdk": "balances.list",
        "tags": [
          "Catálogo"
        ],
        "x-alcance": "saldos:leer",
        "summary": "Sus saldos por moneda",
        "description": "Por moneda: `available` y `reserved`. Requiere el alcance `saldos:leer`.",
        "responses": {
          "200": {
            "description": "Los saldos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListaDeSaldos"
                }
              }
            }
          },
          "401": {
            "description": "La llave falta o no es válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Falta la cabecera Authorization o la llave de API no es válida."
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "La llave es válida pero no tiene el alcance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "scope_missing": {
                    "value": {
                      "error": {
                        "code": "scope_missing",
                        "message": "Su llave no tiene permiso para esta operación."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/payins": {
      "post": {
        "operationId": "crearPayIn",
        "x-sdk": "payins.create",
        "tags": [
          "Cobros"
        ],
        "x-alcance": "payins:crear",
        "summary": "Crear un cobro",
        "description": "Crea un cobro. Con `method: debit_otp`, el banco de su usuario le manda un código y la operación queda `pending` / `awaiting_code` hasta que usted la confirme.\n\nCon `method: incoming_mobile_payment` o `incoming_transfer` usted declara el monto y quién va a pagar, y la operación queda `pending` / `awaiting_payment` hasta que ese pago llegue a la cuenta receptora (la que `GET /v2/capabilities` muestra en `receiving_account`) o venza la ventana (`expires_at`; `expires_in_hours` la acorta, hasta 72 h). Cuando llega un pago que coincide en monto, banco y pagador, la operación pasa a `confirmed` con `bank_reference` y le llega `payin.confirmed`; si vence, `failed` / `expired`. Mientras espera, puede cancelarla con `POST /v2/payins/{id}/cancel`.\n\nRequiere el alcance `payins:crear`.\n\nLos campos de `payer` son los que el método declara en `GET /v2/capabilities`; se validan contra esa misma declaración y un campo que el método no declara se rechaza (`details[].code = unknown`).\n\n**Reintentar este POST es seguro** con la misma `Idempotency-Key`: devuelve el mismo cobro y no pide otro código.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 128
            },
            "description": "Una clave única por pedido, de 8 a 128 caracteres. La misma clave devuelve la misma operación."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NuevoPayIn"
              },
              "examples": {
                "debito_con_codigo": {
                  "value": {
                    "country": "VE",
                    "currency": "VES",
                    "method": "debit_otp",
                    "amount": "1500.50",
                    "payer": {
                      "name": "Nombre Apellido",
                      "document": {
                        "type": "V",
                        "number": "12345678"
                      },
                      "bank_code": "0102",
                      "account_type": "mobile",
                      "account_number": "04121234567"
                    },
                    "reference": "factura 123",
                    "metadata": {
                      "orden": "A-1"
                    }
                  }
                },
                "pago_movil_recibido": {
                  "value": {
                    "country": "VE",
                    "currency": "VES",
                    "method": "incoming_mobile_payment",
                    "amount": "1500.50",
                    "payer": {
                      "document": {
                        "type": "V",
                        "number": "12345678"
                      },
                      "bank_code": "0102",
                      "phone": "04121234567"
                    },
                    "reference": "pedido 8812",
                    "expires_in_hours": 24
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "El cobro existe. `status` dice qué pasó: con `awaiting_code` su usuario tiene que confirmarlo; con `awaiting_payment` se espera el pago hasta `expires_at`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operacion"
                },
                "examples": {
                  "operacion": {
                    "value": {
                      "id": "6f1c2a9e-3b4d-4c5e-8f70-1a2b3c4d5e6f",
                      "type": "payin",
                      "country": "VE",
                      "currency": "VES",
                      "method": "debit_otp",
                      "amount": "1500.50",
                      "status": "pending",
                      "pending_reason": "awaiting_code",
                      "code_expires_at": "2026-09-23T15:04:05Z",
                      "bank_reference": null,
                      "failure": null,
                      "reference": "factura 123",
                      "metadata": {
                        "orden": "A-1"
                      },
                      "created_by": "company",
                      "created_at": "2026-09-23T14:59:05Z",
                      "updated_at": "2026-09-23T14:59:05Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "El pedido no pasó la validación. `details` nombra cada campo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "body_invalid": {
                    "value": {
                      "error": {
                        "code": "body_invalid",
                        "message": "El cuerpo del pedido no se pudo leer."
                      }
                    }
                  },
                  "field_required": {
                    "value": {
                      "error": {
                        "code": "field_required",
                        "message": "Faltan campos obligatorios."
                      }
                    }
                  },
                  "field_invalid": {
                    "value": {
                      "error": {
                        "code": "field_invalid",
                        "message": "Hay campos con un valor inválido."
                      }
                    }
                  },
                  "method_unavailable": {
                    "value": {
                      "error": {
                        "code": "method_unavailable",
                        "message": "El método no está disponible para ese país y moneda."
                      }
                    }
                  },
                  "bank_unsupported": {
                    "value": {
                      "error": {
                        "code": "bank_unsupported",
                        "message": "El banco indicado no admite este método."
                      }
                    }
                  },
                  "amount_out_of_range": {
                    "value": {
                      "error": {
                        "code": "amount_out_of_range",
                        "message": "El monto está fuera del rango permitido."
                      }
                    }
                  },
                  "currency_unsupported": {
                    "value": {
                      "error": {
                        "code": "currency_unsupported",
                        "message": "La moneda no está disponible."
                      }
                    }
                  },
                  "country_unsupported": {
                    "value": {
                      "error": {
                        "code": "country_unsupported",
                        "message": "El país no está disponible."
                      }
                    }
                  },
                  "idempotency_key_required": {
                    "value": {
                      "error": {
                        "code": "idempotency_key_required",
                        "message": "Falta la cabecera Idempotency-Key."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "La llave falta o no es válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Falta la cabecera Authorization o la llave de API no es válida."
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "La llave es válida pero no tiene el alcance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "scope_missing": {
                    "value": {
                      "error": {
                        "code": "scope_missing",
                        "message": "Su llave no tiene permiso para esta operación."
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "La misma `Idempotency-Key` con otro cuerpo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "idempotency_key_reused": {
                    "value": {
                      "error": {
                        "code": "idempotency_key_reused",
                        "message": "La clave de idempotencia ya se usó con otro pedido."
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "No se pudo procesar ahora. Reintente más tarde con la misma `Idempotency-Key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "temporarily_unavailable": {
                    "value": {
                      "error": {
                        "code": "temporarily_unavailable",
                        "message": "No se pudo procesar en este momento. Intente más tarde."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/payins/{id}": {
      "get": {
        "operationId": "verPayIn",
        "x-sdk": "payins.get",
        "tags": [
          "Cobros"
        ],
        "x-alcance": "operaciones:leer",
        "summary": "Consultar un cobro",
        "description": "El estado actual. Requiere el alcance `operaciones:leer`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "El `id` de la operación."
          }
        ],
        "responses": {
          "200": {
            "description": "El cobro.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operacion"
                },
                "examples": {
                  "operacion": {
                    "value": {
                      "id": "6f1c2a9e-3b4d-4c5e-8f70-1a2b3c4d5e6f",
                      "type": "payin",
                      "country": "VE",
                      "currency": "VES",
                      "method": "debit_otp",
                      "amount": "1500.50",
                      "status": "confirmed",
                      "bank_reference": "000123456789",
                      "failure": null,
                      "reference": "factura 123",
                      "metadata": {
                        "orden": "A-1"
                      },
                      "created_by": "company",
                      "created_at": "2026-09-23T14:59:05Z",
                      "updated_at": "2026-09-23T14:59:05Z",
                      "confirmed_at": "2026-09-23T15:04:05Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "La llave falta o no es válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Falta la cabecera Authorization o la llave de API no es válida."
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "La llave es válida pero no tiene el alcance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "scope_missing": {
                    "value": {
                      "error": {
                        "code": "scope_missing",
                        "message": "Su llave no tiene permiso para esta operación."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No hay una operación con ese identificador en su empresa.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "No existe una operación con ese identificador."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/payins/{id}/confirm": {
      "post": {
        "operationId": "confirmarPayIn",
        "x-sdk": "payins.confirm",
        "tags": [
          "Cobros"
        ],
        "x-alcance": "payins:crear",
        "summary": "Confirmar con el código",
        "description": "Ejecuta el débito con el código que tecleó su usuario. El código no se guarda.\n\n`confirmed` trae `bank_reference`. `failed` con `code_rejected` es un código equivocado o vencido: pida otro con `POST /v2/payins/{id}/resend-code` sobre una operación nueva, porque un cobro fallido es final.\n\nRequiere el alcance `payins:crear`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "El `id` de la operación."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Confirmacion"
              },
              "examples": {
                "codigo": {
                  "value": {
                    "code": "123456"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "El resultado del débito.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operacion"
                },
                "examples": {
                  "operacion": {
                    "value": {
                      "id": "6f1c2a9e-3b4d-4c5e-8f70-1a2b3c4d5e6f",
                      "type": "payin",
                      "country": "VE",
                      "currency": "VES",
                      "method": "debit_otp",
                      "amount": "1500.50",
                      "status": "confirmed",
                      "bank_reference": "000123456789",
                      "failure": null,
                      "reference": "factura 123",
                      "metadata": {
                        "orden": "A-1"
                      },
                      "created_by": "company",
                      "created_at": "2026-09-23T14:59:05Z",
                      "updated_at": "2026-09-23T14:59:05Z",
                      "confirmed_at": "2026-09-23T15:04:05Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Falta el código o el cuerpo no se pudo leer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "body_invalid": {
                    "value": {
                      "error": {
                        "code": "body_invalid",
                        "message": "El cuerpo del pedido no se pudo leer."
                      }
                    }
                  },
                  "field_required": {
                    "value": {
                      "error": {
                        "code": "field_required",
                        "message": "Faltan campos obligatorios."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "La llave falta o no es válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Falta la cabecera Authorization o la llave de API no es válida."
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "La llave es válida pero no tiene el alcance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "scope_missing": {
                    "value": {
                      "error": {
                        "code": "scope_missing",
                        "message": "Su llave no tiene permiso para esta operación."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No hay una operación con ese identificador en su empresa.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "No existe una operación con ese identificador."
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "La operación no está esperando un código.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "code_not_expected": {
                    "value": {
                      "error": {
                        "code": "code_not_expected",
                        "message": "La operación no está esperando un código."
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "No se pudo procesar ahora. Reintente más tarde con la misma `Idempotency-Key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "temporarily_unavailable": {
                    "value": {
                      "error": {
                        "code": "temporarily_unavailable",
                        "message": "No se pudo procesar en este momento. Intente más tarde."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/payins/{id}/resend-code": {
      "post": {
        "operationId": "reenviarCodigo",
        "x-sdk": "payins.resendCode",
        "tags": [
          "Cobros"
        ],
        "x-alcance": "payins:crear",
        "summary": "Pedir otro código",
        "description": "Para cuando el mensaje no llegó. Tiene un tope por operación. Requiere el alcance `payins:crear`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "El `id` de la operación."
          }
        ],
        "responses": {
          "200": {
            "description": "La operación, con el nuevo vencimiento del código.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operacion"
                },
                "examples": {
                  "operacion": {
                    "value": {
                      "id": "6f1c2a9e-3b4d-4c5e-8f70-1a2b3c4d5e6f",
                      "type": "payin",
                      "country": "VE",
                      "currency": "VES",
                      "method": "debit_otp",
                      "amount": "1500.50",
                      "status": "pending",
                      "pending_reason": "awaiting_code",
                      "code_expires_at": "2026-09-23T15:04:05Z",
                      "bank_reference": null,
                      "failure": null,
                      "reference": "factura 123",
                      "metadata": {
                        "orden": "A-1"
                      },
                      "created_by": "company",
                      "created_at": "2026-09-23T14:59:05Z",
                      "updated_at": "2026-09-23T14:59:05Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "La llave falta o no es válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Falta la cabecera Authorization o la llave de API no es válida."
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "La llave es válida pero no tiene el alcance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "scope_missing": {
                    "value": {
                      "error": {
                        "code": "scope_missing",
                        "message": "Su llave no tiene permiso para esta operación."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No hay una operación con ese identificador en su empresa.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "No existe una operación con ese identificador."
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "La operación no está esperando un código, o ya se pidieron demasiados.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "code_not_expected": {
                    "value": {
                      "error": {
                        "code": "code_not_expected",
                        "message": "La operación no está esperando un código."
                      }
                    }
                  },
                  "too_many_codes": {
                    "value": {
                      "error": {
                        "code": "too_many_codes",
                        "message": "Se pidieron demasiados códigos para esta operación."
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "No se pudo procesar ahora. Reintente más tarde con la misma `Idempotency-Key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "temporarily_unavailable": {
                    "value": {
                      "error": {
                        "code": "temporarily_unavailable",
                        "message": "No se pudo procesar en este momento. Intente más tarde."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/payins/{id}/cancel": {
      "post": {
        "operationId": "cancelarPayIn",
        "x-sdk": "payins.cancel",
        "tags": [
          "Cobros"
        ],
        "x-alcance": "payins:crear",
        "summary": "Cancelar un cobro que espera el pago",
        "description": "Sólo para un cobro recibido (`incoming_mobile_payment` / `incoming_transfer`) que todavía está `pending` / `awaiting_payment`. Queda `failed` con `failure.code = cancelled` y le llega `payin.failed`. Un pago que llegue después ya no se le atribuye.\n\nCualquier otro cobro, o uno que ya terminó, **409** `not_cancellable`.\n\nRequiere el alcance `payins:crear`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "El `id` de la operación."
          }
        ],
        "responses": {
          "200": {
            "description": "El cobro, cancelado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operacion"
                },
                "examples": {
                  "operacion": {
                    "value": {
                      "id": "6f1c2a9e-3b4d-4c5e-8f70-1a2b3c4d5e6f",
                      "type": "payin",
                      "country": "VE",
                      "currency": "VES",
                      "method": "incoming_mobile_payment",
                      "amount": "1500.50",
                      "status": "failed",
                      "pending_reason": null,
                      "bank_reference": null,
                      "failure": {
                        "code": "cancelled",
                        "message": "La operación fue cancelada."
                      },
                      "reference": "factura 123",
                      "metadata": {
                        "orden": "A-1"
                      },
                      "created_by": "company",
                      "created_at": "2026-09-23T14:59:05Z",
                      "updated_at": "2026-09-23T14:59:05Z",
                      "expires_at": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "La llave falta o no es válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Falta la cabecera Authorization o la llave de API no es válida."
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "La llave es válida pero no tiene el alcance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "scope_missing": {
                    "value": {
                      "error": {
                        "code": "scope_missing",
                        "message": "Su llave no tiene permiso para esta operación."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No hay una operación con ese identificador en su empresa.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "No existe una operación con ese identificador."
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "El cobro no está esperando un pago.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_cancellable": {
                    "value": {
                      "error": {
                        "code": "not_cancellable",
                        "message": "La operación ya no se puede cancelar."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/payouts": {
      "post": {
        "operationId": "crearPayOut",
        "x-sdk": "payouts.create",
        "tags": [
          "Pagos"
        ],
        "x-alcance": "payouts:crear",
        "summary": "Crear un pago",
        "description": "Crea un pago y lo envía. Es UN paso: el monto se reserva de su saldo disponible al crearlo y se descuenta al confirmarse; si el pago falla, vuelve a estar disponible.\n\nRequiere el alcance `payouts:crear`.\n\nLos campos de `beneficiary` son los que el método declara en `GET /v2/capabilities`. `purpose` es opcional: `remittance` por omisión.\n\nEl resultado suele llegar después: la operación queda `pending` / `processing` y el desenlace le llega por webhook (`payout.confirmed` / `payout.failed`) o consultándola.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 128
            },
            "description": "Una clave única por pedido, de 8 a 128 caracteres. La misma clave devuelve la misma operación."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NuevoPayOut"
              },
              "examples": {
                "pago_movil": {
                  "value": {
                    "country": "VE",
                    "currency": "VES",
                    "method": "mobile_payment",
                    "amount": "1500.50",
                    "beneficiary": {
                      "name": "Nombre Apellido",
                      "document": {
                        "type": "V",
                        "number": "12345678"
                      },
                      "bank_code": "0102",
                      "account_number": "04121234567"
                    },
                    "purpose": "remittance",
                    "reference": "remesa 456",
                    "metadata": {
                      "orden": "B-2"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "El pago existe. `status` dice qué pasó.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operacion"
                },
                "examples": {
                  "operacion": {
                    "value": {
                      "id": "6f1c2a9e-3b4d-4c5e-8f70-1a2b3c4d5e6f",
                      "type": "payout",
                      "country": "VE",
                      "currency": "VES",
                      "method": "mobile_payment",
                      "amount": "1500.50",
                      "status": "pending",
                      "pending_reason": "processing",
                      "bank_reference": null,
                      "failure": null,
                      "reference": "factura 123",
                      "metadata": {
                        "orden": "A-1"
                      },
                      "created_by": "company",
                      "created_at": "2026-09-23T14:59:05Z",
                      "updated_at": "2026-09-23T14:59:05Z",
                      "purpose": "remittance"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "El pedido no pasó la validación. `details` nombra cada campo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "body_invalid": {
                    "value": {
                      "error": {
                        "code": "body_invalid",
                        "message": "El cuerpo del pedido no se pudo leer."
                      }
                    }
                  },
                  "field_required": {
                    "value": {
                      "error": {
                        "code": "field_required",
                        "message": "Faltan campos obligatorios."
                      }
                    }
                  },
                  "field_invalid": {
                    "value": {
                      "error": {
                        "code": "field_invalid",
                        "message": "Hay campos con un valor inválido."
                      }
                    }
                  },
                  "method_unavailable": {
                    "value": {
                      "error": {
                        "code": "method_unavailable",
                        "message": "El método no está disponible para ese país y moneda."
                      }
                    }
                  },
                  "bank_unsupported": {
                    "value": {
                      "error": {
                        "code": "bank_unsupported",
                        "message": "El banco indicado no admite este método."
                      }
                    }
                  },
                  "amount_out_of_range": {
                    "value": {
                      "error": {
                        "code": "amount_out_of_range",
                        "message": "El monto está fuera del rango permitido."
                      }
                    }
                  },
                  "currency_unsupported": {
                    "value": {
                      "error": {
                        "code": "currency_unsupported",
                        "message": "La moneda no está disponible."
                      }
                    }
                  },
                  "country_unsupported": {
                    "value": {
                      "error": {
                        "code": "country_unsupported",
                        "message": "El país no está disponible."
                      }
                    }
                  },
                  "idempotency_key_required": {
                    "value": {
                      "error": {
                        "code": "idempotency_key_required",
                        "message": "Falta la cabecera Idempotency-Key."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "La llave falta o no es válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Falta la cabecera Authorization o la llave de API no es válida."
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "La llave es válida pero no tiene el alcance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "scope_missing": {
                    "value": {
                      "error": {
                        "code": "scope_missing",
                        "message": "Su llave no tiene permiso para esta operación."
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "La misma `Idempotency-Key` con otro cuerpo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "idempotency_key_reused": {
                    "value": {
                      "error": {
                        "code": "idempotency_key_reused",
                        "message": "La clave de idempotencia ya se usó con otro pedido."
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "No se pudo procesar ahora. Reintente más tarde con la misma `Idempotency-Key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "temporarily_unavailable": {
                    "value": {
                      "error": {
                        "code": "temporarily_unavailable",
                        "message": "No se pudo procesar en este momento. Intente más tarde."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/payouts/{id}": {
      "get": {
        "operationId": "verPayOut",
        "x-sdk": "payouts.get",
        "tags": [
          "Pagos"
        ],
        "x-alcance": "operaciones:leer",
        "summary": "Consultar un pago",
        "description": "El estado actual. Requiere el alcance `operaciones:leer`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "El `id` de la operación."
          }
        ],
        "responses": {
          "200": {
            "description": "El pago.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operacion"
                },
                "examples": {
                  "operacion": {
                    "value": {
                      "id": "6f1c2a9e-3b4d-4c5e-8f70-1a2b3c4d5e6f",
                      "type": "payout",
                      "country": "VE",
                      "currency": "VES",
                      "method": "mobile_payment",
                      "amount": "1500.50",
                      "status": "confirmed",
                      "bank_reference": "000123456789",
                      "failure": null,
                      "reference": "factura 123",
                      "metadata": {
                        "orden": "A-1"
                      },
                      "created_by": "company",
                      "created_at": "2026-09-23T14:59:05Z",
                      "updated_at": "2026-09-23T14:59:05Z",
                      "purpose": "remittance"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "La llave falta o no es válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Falta la cabecera Authorization o la llave de API no es válida."
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "La llave es válida pero no tiene el alcance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "scope_missing": {
                    "value": {
                      "error": {
                        "code": "scope_missing",
                        "message": "Su llave no tiene permiso para esta operación."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No hay una operación con ese identificador en su empresa.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "No existe una operación con ese identificador."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/payouts/{id}/cancel": {
      "post": {
        "operationId": "cancelarPayOut",
        "x-sdk": "payouts.cancel",
        "tags": [
          "Pagos"
        ],
        "x-alcance": "payouts:crear",
        "summary": "Cancelar un pago pendiente",
        "description": "Pide cancelar un pago que todavía está `pending` / `processing`. Sólo si el último estado conocido lo permite; si el pago ya se ejecutó o ya terminó, **409** `not_cancellable`.\n\nCancelado, el pago queda `failed` con `failure.code = cancelled`, el monto vuelve a su saldo disponible y le llega `payout.failed`. Si no se pudo confirmar la cancelación en el momento, **503** `temporarily_unavailable`: el pago sigue pendiente y puede volver a intentar.\n\nRequiere el alcance `payouts:crear`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "El `id` de la operación."
          }
        ],
        "responses": {
          "200": {
            "description": "El pago, cancelado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operacion"
                },
                "examples": {
                  "operacion": {
                    "value": {
                      "id": "6f1c2a9e-3b4d-4c5e-8f70-1a2b3c4d5e6f",
                      "type": "payout",
                      "country": "VE",
                      "currency": "VES",
                      "method": "mobile_payment",
                      "amount": "1500.50",
                      "status": "failed",
                      "pending_reason": null,
                      "bank_reference": null,
                      "failure": {
                        "code": "cancelled",
                        "message": "La operación fue cancelada."
                      },
                      "reference": "factura 123",
                      "metadata": {
                        "orden": "A-1"
                      },
                      "created_by": "company",
                      "created_at": "2026-09-23T14:59:05Z",
                      "updated_at": "2026-09-23T14:59:05Z",
                      "purpose": "remittance"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "La llave falta o no es válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Falta la cabecera Authorization o la llave de API no es válida."
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "La llave es válida pero no tiene el alcance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "scope_missing": {
                    "value": {
                      "error": {
                        "code": "scope_missing",
                        "message": "Su llave no tiene permiso para esta operación."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No hay una operación con ese identificador en su empresa.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "No existe una operación con ese identificador."
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "El pago ya no se puede cancelar.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_cancellable": {
                    "value": {
                      "error": {
                        "code": "not_cancellable",
                        "message": "La operación ya no se puede cancelar."
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "La cancelación no se pudo confirmar ahora. El pago sigue pendiente; reintente.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "temporarily_unavailable": {
                    "value": {
                      "error": {
                        "code": "temporarily_unavailable",
                        "message": "No se pudo procesar en este momento. Intente más tarde."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/transactions/{id}": {
      "get": {
        "operationId": "verTransaccion",
        "x-sdk": "transactions.get",
        "tags": [
          "Eventos"
        ],
        "x-alcance": "operaciones:leer",
        "summary": "Consultar cualquier operación",
        "description": "Un cobro o un pago por su `id`, con la misma forma. Requiere el alcance `operaciones:leer`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "El `id` de la operación."
          }
        ],
        "responses": {
          "200": {
            "description": "La operación.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operacion"
                },
                "examples": {
                  "operacion": {
                    "value": {
                      "id": "6f1c2a9e-3b4d-4c5e-8f70-1a2b3c4d5e6f",
                      "type": "payin",
                      "country": "VE",
                      "currency": "VES",
                      "method": "debit_otp",
                      "amount": "1500.50",
                      "status": "confirmed",
                      "bank_reference": "000123456789",
                      "failure": null,
                      "reference": "factura 123",
                      "metadata": {
                        "orden": "A-1"
                      },
                      "created_by": "company",
                      "created_at": "2026-09-23T14:59:05Z",
                      "updated_at": "2026-09-23T14:59:05Z",
                      "confirmed_at": "2026-09-23T15:04:05Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "La llave falta o no es válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Falta la cabecera Authorization o la llave de API no es válida."
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "La llave es válida pero no tiene el alcance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "scope_missing": {
                    "value": {
                      "error": {
                        "code": "scope_missing",
                        "message": "Su llave no tiene permiso para esta operación."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No hay una operación con ese identificador en su empresa.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "No existe una operación con ese identificador."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/events": {
      "get": {
        "operationId": "listarEventos",
        "x-sdk": "events.list",
        "tags": [
          "Eventos"
        ],
        "x-alcance": "operaciones:leer",
        "summary": "Los eventos de sus operaciones",
        "description": "El mismo evento que viaja por webhook, en orden, con cursor. Sirve para reponerse de un webhook perdido: guarde el último `next_cursor` y pida desde ahí. Requiere el alcance `operaciones:leer`.",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "El `next_cursor` de la respuesta anterior. Sin él, desde el principio."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Los eventos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListaDeEventos"
                },
                "examples": {
                  "eventos": {
                    "value": {
                      "events": [
                        {
                          "id": "0c9d1b2e-7f3a-4b8c-9d0e-1f2a3b4c5d6e",
                          "type": "payout.confirmed",
                          "occurred_at": "2026-09-23T15:04:05Z",
                          "data": {
                            "id": "6f1c2a9e-3b4d-4c5e-8f70-1a2b3c4d5e6f",
                            "type": "payout",
                            "country": "VE",
                            "currency": "VES",
                            "method": "mobile_payment",
                            "amount": "1500.50",
                            "status": "confirmed",
                            "bank_reference": "000123456789",
                            "failure": null,
                            "reference": "factura 123",
                            "metadata": {
                              "orden": "A-1"
                            },
                            "created_by": "company",
                            "created_at": "2026-09-23T14:59:05Z",
                            "updated_at": "2026-09-23T14:59:05Z",
                            "purpose": "remittance"
                          }
                        }
                      ],
                      "next_cursor": "42"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Un parámetro inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "field_invalid": {
                    "value": {
                      "error": {
                        "code": "field_invalid",
                        "message": "Hay campos con un valor inválido."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "La llave falta o no es válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Falta la cabecera Authorization o la llave de API no es válida."
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "La llave es válida pero no tiene el alcance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "scope_missing": {
                    "value": {
                      "error": {
                        "code": "scope_missing",
                        "message": "Su llave no tiene permiso para esta operación."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/webhook-endpoints": {
      "post": {
        "operationId": "registrarWebhook",
        "x-sdk": "webhookEndpoints.create",
        "tags": [
          "Eventos"
        ],
        "x-alcance": "webhooks:configurar",
        "summary": "Registrar la url de avisos",
        "description": "Registra la url a la que se le mandan los eventos y devuelve el **secreto de firma una sola vez**. Una url activa por empresa: registrar otra la reemplaza, y el secreto anterior sigue valiendo 24 horas para que el cambio no pierda avisos.\n\nRequiere el alcance `webhooks:configurar`. La url tiene que ser `https`.\n\n## Cómo llega un evento\n\n`POST` a su url con el cuerpo de `Evento` y estas cabeceras: `X-Firma` (HMAC-SHA256 de `<X-Timestamp>.<cuerpo>` con su secreto, como `v1=<hex>`; durante una rotación van dos, separadas por espacio), `X-Timestamp` (segundos Unix), `X-Evento` (el `type`) y `X-Id-Evento` (el `id`). Conteste 2xx en menos de 10 segundos; si no, se reintenta con espera creciente durante unas 24 horas (30 s, 2, 10, 30 min, 1, 2, 4, 8 y 8 h, con un ±10 % al azar) y recién entonces se agota; un aviso agotado se reenvía a mano. Deduplique por `id`.\n\nMientras no registre nada acá, los eventos van a la url y con el secreto de la versión 1, si los tiene.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NuevoWebhookEndpoint"
              },
              "examples": {
                "url": {
                  "value": {
                    "url": "https://api.suempresa.com/avisos"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registrada. Guarde `secret`: no se vuelve a mostrar.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "400": {
            "description": "Falta la url o no es https.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "body_invalid": {
                    "value": {
                      "error": {
                        "code": "body_invalid",
                        "message": "El cuerpo del pedido no se pudo leer."
                      }
                    }
                  },
                  "field_required": {
                    "value": {
                      "error": {
                        "code": "field_required",
                        "message": "Faltan campos obligatorios."
                      }
                    }
                  },
                  "field_invalid": {
                    "value": {
                      "error": {
                        "code": "field_invalid",
                        "message": "Hay campos con un valor inválido."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "La llave falta o no es válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Falta la cabecera Authorization o la llave de API no es válida."
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "La llave es válida pero no tiene el alcance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "scope_missing": {
                    "value": {
                      "error": {
                        "code": "scope_missing",
                        "message": "Su llave no tiene permiso para esta operación."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/openapi.json": {
      "get": {
        "operationId": "verContratoV2",
        "tags": [
          "Contrato"
        ],
        "summary": "Descargar este contrato",
        "security": [],
        "description": "Devuelve este mismo documento OpenAPI, servido por la API. **No requiere autenticación.**",
        "responses": {
          "200": {
            "description": "El contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/v2/llms.txt": {
      "get": {
        "operationId": "verIndiceParaAsistentes",
        "tags": [
          "Contrato"
        ],
        "summary": "El índice de la documentación para asistentes de IA",
        "security": [],
        "description": "`llms.txt` (convención de llmstxt.org): qué hay y dónde, en Markdown. Cada página de la documentación existe también en `/v2/docs/{page}`. **No requiere autenticación.**",
        "responses": {
          "200": {
            "description": "El índice, en Markdown.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v2/llms-full.txt": {
      "get": {
        "operationId": "verDocumentacionCompleta",
        "tags": [
          "Contrato"
        ],
        "summary": "Toda la documentación en un archivo",
        "security": [],
        "description": "Las páginas de `/v2/docs/{page}` concatenadas, para leerlas de una vez. **No requiere autenticación.**",
        "responses": {
          "200": {
            "description": "La documentación entera, en Markdown.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v2/docs": {
      "get": {
        "operationId": "verDocumentacionWeb",
        "tags": [
          "Contrato"
        ],
        "summary": "La documentación para leer en el navegador",
        "security": [],
        "description": "Redirige (`302`) a la primera página de la documentación para leer en el navegador. `Location` es relativa (`docs/empezar`), así que la misma ruta sirve bajo el sandbox. **No requiere autenticación.**",
        "responses": {
          "302": {
            "description": "A la página «Empezar».",
            "headers": {
              "Location": {
                "description": "Relativa a la ruta pedida: `docs/empezar`.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v2/docs/": {
      "get": {
        "operationId": "verDocumentacionWebConBarra",
        "tags": [
          "Contrato"
        ],
        "summary": "Lo mismo que `/v2/docs`, con barra final",
        "security": [],
        "description": "Redirige (`302`) a la primera página de la documentación para leer en el navegador. `Location` es relativa (`empezar`), así que la misma ruta sirve bajo el sandbox. **No requiere autenticación.**",
        "responses": {
          "302": {
            "description": "A la página «Empezar».",
            "headers": {
              "Location": {
                "description": "Relativa a la ruta pedida: `empezar`.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v2/docs/{page}": {
      "get": {
        "operationId": "verPagina",
        "tags": [
          "Contrato"
        ],
        "summary": "Una página de la documentación, en Markdown o en HTML",
        "security": [],
        "description": "Con `.md` (`cobros.md`), la página en Markdown, para asistentes y herramientas. Sin extensión (`cobros`), la misma página en HTML, para leer en el navegador; `docs.css` y `docs.js` son su hoja de estilos y su script. La lista de páginas está en `/v2/llms.txt`. **No requiere autenticación.**",
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^([a-z0-9-]+(\\.md)?|docs\\.css|docs\\.js)$"
            },
            "description": "El nombre de la página: con `.md` para Markdown, sin extensión para HTML; o `docs.css`, `docs.js`."
          }
        ],
        "responses": {
          "200": {
            "description": "La página (Markdown o HTML), o el estático pedido.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              },
              "text/css": {
                "schema": {
                  "type": "string"
                }
              },
              "text/javascript": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No hay una página con ese nombre.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "route_not_found": {
                    "value": {
                      "error": {
                        "code": "route_not_found",
                        "message": "La ruta no existe."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "LlaveDeEmpresa": {
        "type": "http",
        "scheme": "bearer",
        "description": "Su llave de API, en `Authorization: Bearer <llave>`: `tuc_live_…` en producción, `tuc_test_…` en el sandbox (`https://api.tucapi.app/sandbox`).\n\n| Alcance | Permite |\n|---|---|\n| `payins:crear` | crear, confirmar y pedir código |\n| `payouts:crear` | crear pagos |\n| `operaciones:leer` | consultar operaciones y eventos |\n| `saldos:leer` | `GET /v2/balances` |\n| `webhooks:configurar` | registrar la url de avisos |\n\nEl catálogo (`capabilities`, `methods`, `banks`) lo lee cualquier llave válida."
      }
    },
    "schemas": {
      "NuevoPayIn": {
        "type": "object",
        "required": [
          "country",
          "currency",
          "method",
          "amount",
          "payer"
        ],
        "properties": {
          "country": {
            "type": "string",
            "pattern": "^[A-Za-z]{2}$",
            "description": "ISO 3166-1 alfa-2.",
            "examples": [
              "VE"
            ]
          },
          "currency": {
            "type": "string",
            "pattern": "^[A-Za-z]{3}$",
            "description": "ISO 4217.",
            "examples": [
              "VES"
            ]
          },
          "method": {
            "$ref": "#/components/schemas/CodigoDeMetodo"
          },
          "amount": {
            "type": "string",
            "pattern": "^[0-9]+(\\.[0-9]+)?$",
            "description": "Cadena decimal con punto y los decimales de la moneda. Nunca un número JSON.",
            "examples": [
              "1500.50"
            ]
          },
          "payer": {
            "$ref": "#/components/schemas/Pagador"
          },
          "mandate": {
            "$ref": "#/components/schemas/Mandato"
          },
          "reference": {
            "type": "string",
            "maxLength": 200,
            "description": "Su referencia. Vuelve tal cual en la operación."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Lo que usted quiera guardar, hasta 4 KB. Vuelve tal cual."
          },
          "expires_in_hours": {
            "type": "integer",
            "minimum": 1,
            "maximum": 72,
            "description": "Sólo cobros recibidos: cuántas horas se espera el pago. Sin él, la ventana por omisión (48 h). Nunca más de 72."
          }
        }
      },
      "Pagador": {
        "type": "object",
        "additionalProperties": true,
        "description": "Los campos que el método declara en `GET /v2/capabilities` (`fields`). Los de un objeto anidado se declaran con punto: `document.type` es `{\"document\": {\"type\": …}}`. En un cobro recibido son los datos de QUIÉN VA A PAGAR (documento, banco y, en Pago Móvil, teléfono): con ellos se reconoce su pago cuando llega.",
        "properties": {
          "name": {
            "type": "string"
          },
          "document": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string"
              },
              "number": {
                "type": "string"
              }
            }
          },
          "bank_code": {
            "type": "string"
          },
          "account_type": {
            "type": "string",
            "enum": [
              "mobile",
              "account"
            ]
          },
          "account_number": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "email": {
            "type": "string"
          }
        }
      },
      "Mandato": {
        "type": "object",
        "description": "Sólo para `direct_debit`: el contrato de domiciliación ya autorizado en el banco del pagador.",
        "properties": {
          "contract_id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{1,30}$",
            "description": "Alfanumérico, hasta 30: es lo que acepta el banco."
          },
          "contract_date": {
            "type": "string",
            "format": "date"
          }
        }
      },
      "NuevoPayOut": {
        "type": "object",
        "required": [
          "country",
          "currency",
          "method",
          "amount",
          "beneficiary"
        ],
        "properties": {
          "country": {
            "type": "string",
            "pattern": "^[A-Za-z]{2}$",
            "description": "ISO 3166-1 alfa-2.",
            "examples": [
              "VE"
            ]
          },
          "currency": {
            "type": "string",
            "pattern": "^[A-Za-z]{3}$",
            "description": "ISO 4217.",
            "examples": [
              "VES"
            ]
          },
          "method": {
            "$ref": "#/components/schemas/CodigoDeMetodo"
          },
          "amount": {
            "type": "string",
            "pattern": "^[0-9]+(\\.[0-9]+)?$",
            "description": "Cadena decimal con punto y los decimales de la moneda.",
            "examples": [
              "1500.50"
            ]
          },
          "beneficiary": {
            "$ref": "#/components/schemas/Beneficiario"
          },
          "purpose": {
            "$ref": "#/components/schemas/Proposito"
          },
          "reference": {
            "type": "string",
            "maxLength": 200,
            "description": "Su referencia. Vuelve tal cual en la operación."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Lo que usted quiera guardar, hasta 4 KB. Vuelve tal cual."
          }
        }
      },
      "Beneficiario": {
        "type": "object",
        "additionalProperties": true,
        "description": "Los campos que el método declara en `GET /v2/capabilities` (`fields`). `mobile_payment` lleva el teléfono en `account_number`; `bank_transfer`, la cuenta de 20 dígitos.",
        "properties": {
          "name": {
            "type": "string"
          },
          "document": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string"
              },
              "number": {
                "type": "string"
              }
            }
          },
          "bank_code": {
            "type": "string"
          },
          "account_number": {
            "type": "string"
          }
        }
      },
      "Proposito": {
        "type": "string",
        "enum": [
          "remittance",
          "payroll"
        ],
        "default": "remittance",
        "description": "Para qué es el pago. `remittance` por omisión."
      },
      "Confirmacion": {
        "type": "object",
        "required": [
          "code"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "El código que recibió su usuario. No se guarda."
          }
        }
      },
      "Operacion": {
        "type": "object",
        "description": "Lo que usted ve de una operación. Misma forma para pay-ins y payouts.",
        "required": [
          "id",
          "type",
          "country",
          "currency",
          "method",
          "amount",
          "status",
          "bank_reference",
          "failure",
          "concept",
          "created_by",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "enum": [
              "payin",
              "payout"
            ]
          },
          "country": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "method": {
            "$ref": "#/components/schemas/CodigoDeMetodo"
          },
          "purpose": {
            "$ref": "#/components/schemas/Proposito"
          },
          "amount": {
            "type": "string",
            "examples": [
              "1500.50"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/Estado"
          },
          "pending_reason": {
            "$ref": "#/components/schemas/MotivoPendiente"
          },
          "code_expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Sólo con `awaiting_code`: hasta cuándo vale el código."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Sólo cobros recibidos: hasta cuándo se espera el pago. Vencido, `failed` / `expired`."
          },
          "confirmed_at": {
            "type": "string",
            "format": "date-time",
            "description": "Cuándo se confirmó. Sólo con `confirmed`."
          },
          "created_by": {
            "$ref": "#/components/schemas/CreadoPor"
          },
          "bank_reference": {
            "type": [
              "string",
              "null"
            ],
            "description": "La referencia del banco: la que la persona ve en su movimiento bancario (en Venezuela, 8 dígitos). null mientras el banco no la dio."
          },
          "failure": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Fallo"
              },
              {
                "type": "null"
              }
            ],
            "description": "Sólo con `failed`."
          },
          "concept": {
            "type": "string",
            "readOnly": true,
            "description": "El concepto que la contraparte ve en su movimiento bancario, por ejemplo «Pago TCP7K2M9Q»: «Pago» o «Cobro», el prefijo de tres letras de su empresa y seis caracteres tomados del id de la operación. Lo pone el sistema; usted no lo manda ni lo cambia. Su `reference` no viaja al banco."
          },
          "reference": {
            "type": "string",
            "description": "Su referencia. Vuelve tal cual en la operación y en los eventos; NO viaja al banco (el concepto lo pone el sistema: ver `concept`)."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Evento": {
        "type": "object",
        "required": [
          "id",
          "type",
          "occurred_at",
          "data"
        ],
        "description": "Lo que viaja por el webhook y lo que devuelve GET /v2/events. Deduplique por `id`.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "$ref": "#/components/schemas/TipoDeEvento"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "$ref": "#/components/schemas/Operacion"
          }
        }
      },
      "TipoDeEvento": {
        "type": "string",
        "enum": [
          "payin.confirmed",
          "payin.failed",
          "payout.confirmed",
          "payout.failed",
          "test.ping"
        ],
        "description": "Los de una operación son sólo finales: `processing`, `awaiting_code` y `awaiting_payment` no generan evento. `test.ping` es el aviso de prueba que usted pide desde el dashboard: su `data` es una operación de EJEMPLO (id en ceros, `pending`, monto `0.00`, `metadata.test = true`) que no corresponde a ningún movimiento; no se reintenta ni aparece en `GET /v2/events`. Contéstelo con `2xx` y no lo procese."
      },
      "ListaDeEventos": {
        "type": "object",
        "required": [
          "events",
          "next_cursor"
        ],
        "properties": {
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Evento"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Para pedir lo que sigue. `null` si no hubo eventos."
          }
        }
      },
      "NuevoWebhookEndpoint": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 500,
            "description": "Tiene que ser https."
          }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "required": [
          "id",
          "url",
          "secret",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string"
          },
          "secret": {
            "type": "string",
            "description": "Para verificar `X-Firma`. Se muestra UNA sola vez."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Estado": {
        "type": "string",
        "enum": [
          "pending",
          "confirmed",
          "failed"
        ],
        "description": "`pending` puede requerir una acción suya (`pending_reason`). `confirmed` y `failed` son finales."
      },
      "MotivoPendiente": {
        "type": "string",
        "enum": [
          "awaiting_code",
          "awaiting_payment",
          "processing"
        ],
        "description": "`awaiting_code`: su usuario tiene que confirmar con el código. `awaiting_payment`: se espera el pago del pagador (cobro recibido). `processing`: enviada, sin resultado final todavía."
      },
      "CreadoPor": {
        "type": "string",
        "enum": [
          "company",
          "operator"
        ],
        "description": "`company`: la creó usted por la API. `operator`: la creó un operador de la plataforma en su nombre (por ejemplo, para atribuirle un pago que llegó sin cobro esperado); usted la ve y la recibe por webhook como cualquier otra."
      },
      "Fallo": {
        "type": "object",
        "required": [
          "code",
          "reason",
          "message"
        ],
        "properties": {
          "code": {
            "$ref": "#/components/schemas/CodigoDeFallo"
          },
          "reason": {
            "$ref": "#/components/schemas/MotivoDeFallo"
          },
          "message": {
            "type": "string",
            "description": "Para mostrar, no para comparar. Depende del motivo."
          }
        },
        "description": "Por qué no salió: `code` es el código principal (fijo, para decidir), `reason` el motivo normalizado debajo (para explicar), `message` un texto legible. Nunca traen el código crudo del proveedor."
      },
      "CodigoDeFallo": {
        "type": "string",
        "enum": [
          "counterparty_rejected",
          "payer_insufficient_funds",
          "mandate_required",
          "code_rejected",
          "limit_exceeded",
          "outside_hours",
          "invalid_data",
          "expired",
          "cancelled",
          "temporarily_unavailable",
          "rejected"
        ],
        "description": "- `counterparty_rejected`: El banco del destinatario rechazó la operación.\n- `payer_insufficient_funds`: El pagador no tiene fondos suficientes.\n- `mandate_required`: El pagador todavía no autorizó el débito en su banco.\n- `code_rejected`: El código no es válido o venció. Pida uno nuevo.\n- `limit_exceeded`: El monto supera el límite permitido.\n- `outside_hours`: Fuera del horario bancario.\n- `invalid_data`: El banco no aceptó los datos de la operación. Revíselos y cree otra.\n- `expired`: La operación venció.\n- `cancelled`: La operación fue cancelada.\n- `temporarily_unavailable`: No se pudo procesar en este momento. Intente más tarde.\n- `rejected`: La operación fue rechazada.\n\nMotivos que admite cada código (`failure.reason`): `counterparty_rejected` → `invalid_account`, `account_closed`, `account_blocked`, `beneficiary_mismatch`, `invalid_phone`, `invalid_bank`, `invalid_document`, `other`; `payer_insufficient_funds` → `payer_insufficient_funds`; `mandate_required` → `no_mandate`, `mandate_revoked`; `code_rejected` → `code_invalid`, `code_expired`; `limit_exceeded` → `amount_over_limit`, `daily_limit`; `outside_hours` → `outside_hours`; `invalid_data` → `invalid_data`; `temporarily_unavailable` → `other`, `bank_offline`, `timeout`; `expired` → `expired`; `cancelled` → `cancelled`; `rejected` → `other`."
      },
      "MotivoDeFallo": {
        "type": "string",
        "enum": [
          "invalid_account",
          "account_closed",
          "account_blocked",
          "beneficiary_mismatch",
          "invalid_phone",
          "invalid_bank",
          "invalid_document",
          "other",
          "payer_insufficient_funds",
          "code_invalid",
          "code_expired",
          "no_mandate",
          "mandate_revoked",
          "amount_over_limit",
          "daily_limit",
          "outside_hours",
          "invalid_data",
          "bank_offline",
          "timeout",
          "expired",
          "cancelled"
        ],
        "description": "El motivo normalizado de un fallo, con el código al que pertenece:\n- `invalid_account` (counterparty_rejected): La cuenta o el teléfono del destinatario no existe en su banco.\n- `account_closed` (counterparty_rejected): La cuenta del destinatario está cerrada.\n- `account_blocked` (counterparty_rejected): La cuenta del destinatario está bloqueada.\n- `beneficiary_mismatch` (counterparty_rejected): Los datos del destinatario no corresponden a esa cuenta.\n- `invalid_phone` (counterparty_rejected): El teléfono del destinatario no es válido para Pago Móvil.\n- `invalid_bank` (counterparty_rejected): El banco del destinatario no existe o no recibe este tipo de operación.\n- `invalid_document` (counterparty_rejected): El documento del destinatario no es válido.\n- `other` (counterparty_rejected, temporarily_unavailable, rejected): Sin más detalle: vale el mensaje del código.\n- `payer_insufficient_funds` (payer_insufficient_funds): El pagador no tiene fondos suficientes.\n- `code_invalid` (code_rejected): El código no es válido.\n- `code_expired` (code_rejected): El código venció.\n- `no_mandate` (mandate_required): El pagador todavía no autorizó el débito en su banco.\n- `mandate_revoked` (mandate_required): El pagador suspendió o revocó la autorización de débito.\n- `amount_over_limit` (limit_exceeded): El monto supera el límite permitido para esta operación.\n- `daily_limit` (limit_exceeded): Se superó el límite diario permitido.\n- `outside_hours` (outside_hours): Fuera del horario bancario.\n- `invalid_data` (invalid_data): El banco no aceptó los datos de la operación.\n- `bank_offline` (temporarily_unavailable): El banco no está disponible en este momento.\n- `timeout` (temporarily_unavailable): El banco no respondió a tiempo.\n- `expired` (expired): La operación venció.\n- `cancelled` (cancelled): La operación fue cancelada."
      },
      "CodigoDeMetodo": {
        "type": "string",
        "enum": [
          "debit_otp",
          "direct_debit",
          "incoming_mobile_payment",
          "incoming_transfer",
          "mobile_payment",
          "bank_transfer"
        ],
        "description": "Un código del catálogo. Los que están disponibles hoy los dice `GET /v2/capabilities`. `incoming_*` son cobros RECIBIDOS: el pagador manda el dinero por su cuenta a la cuenta receptora y usted lo espera con un cobro creado antes."
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "$ref": "#/components/schemas/DetalleDeError"
          }
        },
        "description": "La forma de TODOS los errores de esta API. Siempre JSON, nunca texto plano."
      },
      "DetalleDeError": {
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "$ref": "#/components/schemas/CodigoDeError"
          },
          "message": {
            "type": "string",
            "description": "Para mostrar, no para comparar."
          },
          "details": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Detalle"
            },
            "description": "Sólo en los errores de validación: un ítem por campo."
          }
        }
      },
      "Detalle": {
        "type": "object",
        "required": [
          "field",
          "code"
        ],
        "properties": {
          "field": {
            "type": "string",
            "description": "La ruta del campo, con punto: `payer.document.number`, `Idempotency-Key`.",
            "examples": [
              "payer.document.number"
            ]
          },
          "code": {
            "$ref": "#/components/schemas/CodigoDeDetalle"
          }
        }
      },
      "CodigoDeDetalle": {
        "type": "string",
        "enum": [
          "required",
          "invalid",
          "unknown"
        ]
      },
      "CodigoDeError": {
        "type": "string",
        "enum": [
          "unauthorized",
          "scope_missing",
          "body_invalid",
          "field_required",
          "field_invalid",
          "method_unavailable",
          "bank_unsupported",
          "amount_out_of_range",
          "currency_unsupported",
          "country_unsupported",
          "idempotency_key_reused",
          "idempotency_key_required",
          "not_found",
          "not_cancellable",
          "code_not_expected",
          "too_many_codes",
          "temporarily_unavailable",
          "route_not_found",
          "method_not_allowed"
        ],
        "description": "- `unauthorized`: Falta la cabecera Authorization o la llave de API no es válida.\n- `scope_missing`: Su llave no tiene permiso para esta operación.\n- `body_invalid`: El cuerpo del pedido no se pudo leer.\n- `field_required`: Faltan campos obligatorios.\n- `field_invalid`: Hay campos con un valor inválido.\n- `method_unavailable`: El método no está disponible para ese país y moneda.\n- `bank_unsupported`: El banco indicado no admite este método.\n- `amount_out_of_range`: El monto está fuera del rango permitido.\n- `currency_unsupported`: La moneda no está disponible.\n- `country_unsupported`: El país no está disponible.\n- `idempotency_key_reused`: La clave de idempotencia ya se usó con otro pedido.\n- `idempotency_key_required`: Falta la cabecera Idempotency-Key.\n- `not_found`: No existe una operación con ese identificador.\n- `not_cancellable`: La operación ya no se puede cancelar.\n- `code_not_expected`: La operación no está esperando un código.\n- `too_many_codes`: Se pidieron demasiados códigos para esta operación.\n- `temporarily_unavailable`: No se pudo procesar en este momento. Intente más tarde.\n- `route_not_found`: La ruta no existe.\n- `method_not_allowed`: El método HTTP no está permitido en esta ruta."
      },
      "Capacidades": {
        "type": "object",
        "required": [
          "config_version",
          "countries"
        ],
        "properties": {
          "config_version": {
            "type": "integer",
            "description": "Cambia cuando cambia el catálogo."
          },
          "countries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Pais"
            }
          }
        }
      },
      "Pais": {
        "type": "object",
        "required": [
          "code",
          "name",
          "currencies",
          "payin_methods",
          "payout_methods"
        ],
        "properties": {
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "currencies": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Moneda"
            }
          },
          "payin_methods": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Metodo"
            }
          },
          "payout_methods": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Metodo"
            }
          }
        }
      },
      "Moneda": {
        "type": "object",
        "required": [
          "code",
          "decimals"
        ],
        "properties": {
          "code": {
            "type": "string"
          },
          "decimals": {
            "type": "integer"
          }
        }
      },
      "Metodo": {
        "type": "object",
        "required": [
          "code",
          "name",
          "country",
          "currency",
          "direction",
          "fields",
          "min_amount",
          "max_amount",
          "availability"
        ],
        "properties": {
          "code": {
            "$ref": "#/components/schemas/CodigoDeMetodo"
          },
          "name": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "payin",
              "payout"
            ]
          },
          "fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Campo"
            },
            "description": "Lo que `payer` (o `beneficiary`) tiene que traer para este método."
          },
          "min_amount": {
            "type": "string"
          },
          "max_amount": {
            "type": [
              "string",
              "null"
            ],
            "description": "`null` = sin tope."
          },
          "availability": {
            "$ref": "#/components/schemas/Disponibilidad"
          },
          "receiving_account": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CuentaReceptora"
              },
              {
                "type": "null"
              }
            ],
            "description": "Sólo cobros recibidos: adónde tiene que pagar su pagador. Muéstreselo tal cual. Ausente si todavía no está cargada."
          }
        }
      },
      "CuentaReceptora": {
        "type": "object",
        "required": [
          "bank_code",
          "document",
          "holder"
        ],
        "properties": {
          "bank_code": {
            "type": "string",
            "description": "El banco de la cuenta receptora."
          },
          "phone": {
            "type": "string",
            "description": "Sólo `incoming_mobile_payment`: el teléfono al que se manda el Pago Móvil."
          },
          "account_number": {
            "type": "string",
            "description": "Sólo `incoming_transfer`: la cuenta de 20 dígitos."
          },
          "document": {
            "type": "string",
            "description": "El documento del titular, como lo pide el banco emisor."
          },
          "holder": {
            "type": "string",
            "description": "El nombre del titular."
          }
        }
      },
      "Campo": {
        "type": "object",
        "required": [
          "name",
          "type",
          "required"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "La ruta dentro de `payer`, con punto para los anidados."
          },
          "type": {
            "type": "string",
            "enum": [
              "string",
              "enum",
              "date"
            ]
          },
          "required": {
            "type": "boolean"
          },
          "pattern": {
            "type": "string"
          },
          "enum": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "example": {
            "type": "string"
          }
        }
      },
      "Disponibilidad": {
        "type": "string",
        "enum": [
          "available",
          "temporarily_unavailable",
          "disabled"
        ]
      },
      "ListaDeMetodos": {
        "type": "object",
        "required": [
          "methods"
        ],
        "properties": {
          "methods": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Metodo"
            }
          }
        }
      },
      "Banco": {
        "type": "object",
        "required": [
          "code",
          "name",
          "methods"
        ],
        "properties": {
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "methods": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CodigoDeMetodo"
            },
            "description": "Los que admite hoy."
          }
        }
      },
      "ListaDeBancos": {
        "type": "object",
        "required": [
          "banks"
        ],
        "properties": {
          "banks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Banco"
            }
          }
        }
      },
      "Saldo": {
        "type": "object",
        "required": [
          "currency",
          "available",
          "reserved"
        ],
        "properties": {
          "currency": {
            "type": "string"
          },
          "available": {
            "type": "string"
          },
          "reserved": {
            "type": "string"
          }
        }
      },
      "ListaDeSaldos": {
        "type": "object",
        "required": [
          "balances"
        ],
        "properties": {
          "balances": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Saldo"
            }
          }
        }
      }
    }
  }
}
