> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Configurar el riel

> Aprovisiona Pix Directo vía JD desde cero: la cadena del ledger de Midaz, los registros de titulares en el CRM, las veinte rutas contables, el vínculo de integración con JD que lleva tu ISPB y las verificaciones que prueban que cada paso quedó aplicado.

Un despliegue nuevo de Pix Directo vía JD arranca, responde a su sonda de salud y rechaza todos los pagos. Nada está roto: el riel necesita que exista una cadena de objetos antes de poder mover dinero, y hasta que existan falla cerrado en lugar de adivinar.

Esta página es esa cadena, en orden, con la falla que produce cada eslabón ausente. Lerian ejecuta este aprovisionamiento contigo durante el onboarding. Usa la página para saber qué tiene que existir, qué significa cada valor y cómo probar que quedó aplicado.

<Note>
  Esta cadena de aprovisionamiento se aplica a un **despliegue single-tenant**. En la oferta gestionada multi-tenant (SaaS), la plataforma aprovisiona y resuelve todo — credenciales, vínculos, CRM y los servicios con los que habla el riel — de forma automática por tenant; ninguno de estos pasos te toca ejecutarlo.
</Note>

<Note>
  El orden es una cadena de dependencias, no una preferencia. Midaz rechaza cada eslabón mientras falte el anterior, y una clave de systemplane tiene que estar escrita antes de que un pago pueda materializar los límites de transacción. Donde un paso puede ejecutarse en cualquier orden, la página lo dice.
</Note>

## Antes de empezar

Hablas con tres servicios, y confundirlos es el primer error más común.

| Servicio                              | Dirección                                       | Las rutas empiezan con       | Header adicional    |
| ------------------------------------- | ----------------------------------------------- | ---------------------------- | ------------------- |
| Ledger (Midaz)                        | `MIDAZ_URL_ONBOARDING`, `MIDAZ_URL_TRANSACTION` | `/v1/organizations/...`      | —                   |
| CRM (titulares de cuenta)             | `CRM_URL`                                       | `/v1/holders`, `/v1/aliases` | `X-Organization-Id` |
| El plano de administración del plugin | la dirección propia del plugin                  | `/system/...`                | —                   |

También necesitas:

* Un Bearer token para el ledger, uno para el CRM y uno para el plugin. Pueden venir de audiencias distintas.
* El permiso `systemplane:write` en el token del plugin. Sin él, las escrituras de configuración responden `403`.
* `SYSTEMPLANE_ENABLED=true` en el plugin. Si queda sin definir, el grupo de rutas `/system` no se monta en absoluto y todas las escrituras de configuración responden `404`.
* El **ISPB** de tu institución: el identificador de 8 dígitos con el que te acreditaron en BACEN.

Los ejemplos siguientes usan estas variables de shell. Donde el riel ya tiene un nombre para un valor, la variable lleva ese mismo nombre, así que lo que lees aquí es lo que defines en el momento del despliegue. Cada UUID, documento e ISPB es un marcador de posición — usa los valores que devuelve tu propio entorno.

```bash theme={null}
# Addresses. The first two are this rail's own deployment variables.
MIDAZ_URL_ONBOARDING="https://midaz-onboarding.example.com"
MIDAZ_URL_TRANSACTION="https://midaz-transaction.example.com"
CRM_URL="https://crm.example.com"
PIX_JD_BASE_URL="https://pix-jd.example.com"       # this deployment's own address

# Three different bearers. They are not interchangeable.
MIDAZ_BEARER_TOKEN="..."                           # for the ledger
CRM_BEARER_TOKEN="..."                             # for the CRM
PIX_JD_BEARER_TOKEN="..."                          # for the plugin, needs systemplane:write

MIDAZ_HEADERS=(-H "Authorization: Bearer $MIDAZ_BEARER_TOKEN"
               -H 'Content-Type: application/json')
```

<Note>
  Midaz expone una superficie de onboarding y una superficie de transacción, y este riel las configura por separado. Un despliegue que sirve ambas desde una sola dirección da el mismo valor a las dos variables. Las llamadas siguientes están agrupadas por el objeto que crea cada una.
</Note>

<Warning>
  El CRM exige el header `X-Organization-Id` en cada ruta de colección. Sin él, una consulta no queda acotada a tu organización, y lo que vuelve es otro registro o nada en absoluto.
</Warning>

## Aprovisiona el ledger y los registros de titulares

Estos pasos se ejecutan contra Midaz y el CRM. Crean las cuentas en las que el riel registra los asientos y los registros de titulares desde los que resuelve las contrapartes.

<Steps>
  <Step title="Crear la organización y el ledger">
    La organización es tu institución en los libros. El ledger es el libro en el que registra. Una institución puede tener más de un ledger; este riel registra en exactamente uno.

    ```bash theme={null}
    curl -s -X POST "$MIDAZ_URL_ONBOARDING/v1/organizations" "${MIDAZ_HEADERS[@]}" -d '{
      "legalName": "Example Institution",
      "legalDocument": "12345678000199",
      "address": { "country": "BR" }
    }'
    # -> {"id": "..."}  keep it as $MIDAZ_ORGANIZATION_ID

    curl -s -X POST "$MIDAZ_URL_ONBOARDING/v1/organizations/$MIDAZ_ORGANIZATION_ID/ledgers" "${MIDAZ_HEADERS[@]}" -d '{
      "name": "Pix ledger"
    }'
    # -> {"id": "..."}  keep it as $MIDAZ_LEDGER_ID
    ```

    Un `201` sin `id` en el cuerpo no es un éxito: nada puede referenciar lo que se creó, ni tú ni la limpieza posterior. Detente ahí en lugar de llevar un id vacío al paso siguiente.
  </Step>

  <Step title="Crear el activo BRL y esperar a que aparezca">
    El activo es la moneda en la que se registra el dinero. Para Pix es `BRL`.

    ```bash theme={null}
    curl -s -X POST "$MIDAZ_URL_ONBOARDING/v1/organizations/$MIDAZ_ORGANIZATION_ID/ledgers/$MIDAZ_LEDGER_ID/assets" "${MIDAZ_HEADERS[@]}" -d '{
      "name": "BRL",
      "type": "currency",
      "code": "BRL"
    }'
    ```

    Tanto `201` como `409` son buenas respuestas. Un activo se identifica por su código dentro de un ledger, así que "ya existe" es indistinguible del éxito.

    Aquí pasan dos cosas, y la segunda es fácil de pasar por alto:

    1. El ledger empieza a aceptar cuentas en ese activo. Antes de esto, rechaza todas las cuentas con `0034 Asset Code Not Found`.
    2. Midaz crea la cuenta **`@external/BRL`** junto a él. Es la única cuenta de un libro que puede debitarse sin haber sido acreditada antes, así que de ahí sale el saldo de apertura y es la contraparte de cada liquidación con el mundo exterior.

    <Warning>
      El `201` llega antes de que el activo aparezca en el listado. Un script que crea el activo y crea una cuenta en la línea siguiente es exactamente lo que falla de forma intermitente. Sondea el listado hasta que aparezca el código, con un plazo límite:

      ```bash theme={null}
      deadline=$(( $(date +%s) + 12 ))
      until curl -s "$MIDAZ_URL_ONBOARDING/v1/organizations/$MIDAZ_ORGANIZATION_ID/ledgers/$MIDAZ_LEDGER_ID/assets" "${MIDAZ_HEADERS[@]}" \
            | jq -e '.items[]? | select(.code=="BRL")' >/dev/null; do
        if [ "$(date +%s)" -ge "$deadline" ]; then
          echo "aborted: BRL did not appear in the listing within 12s" >&2
          echo "do not create the account: it would be refused with 0034" >&2
          exit 1
        fi
        sleep 0.15
      done
      ```
    </Warning>
  </Step>

  <Step title="Crear una cuenta por rol">
    Crea una cuenta para cada rol que ejercita tu entorno — pagador, receptor y cualquier otro que tenga tu producto.

    ```bash theme={null}
    curl -s -X POST "$MIDAZ_URL_ONBOARDING/v1/organizations/$MIDAZ_ORGANIZATION_ID/ledgers/$MIDAZ_LEDGER_ID/accounts" "${MIDAZ_HEADERS[@]}" -d '{
      "name": "payer account",
      "assetCode": "BRL",
      "type": "deposit",
      "alias": "@payer",
      "status": { "code": "ACTIVE" }
    }'
    # -> {"id": "..."}  keep it as $MIDAZ_ACCOUNT_ID
    ```

    El id de la cuenta se consume dos veces más adelante: en el vínculo del CRM de abajo y como el `accountId` de cada llamada de negocio que haces contra el plugin. El **alias** (`@payer`) es el nombre corto con el que se lee el libro y se registra en él, y reaparece en el paso siguiente en el lugar menos esperado.
  </Step>

  <Step title="Crear el titular en el CRM">
    El titular es el dueño de la cuenta. El plugin resuelve **quién** es una contraparte desde el CRM, no desde el ledger.

    ```bash theme={null}
    curl -s -X POST "$CRM_URL/v1/holders" \
      -H "Authorization: Bearer $CRM_BEARER_TOKEN" -H 'Content-Type: application/json' \
      -H "X-Organization-Id: $MIDAZ_ORGANIZATION_ID" -d '{
      "type": "NATURAL_PERSON",
      "document": "12345678909",
      "name": "Example Person",
      "externalId": "@payer",
      "addresses": { "primary": { "city": "SAO PAULO" } }
    }'
    # -> {"id": "..."}  keep it as $CRM_HOLDER_ID
    ```

    Cuatro campos, tres trampas:

    * **`type` sigue la longitud del documento.** `NATURAL_PERSON` para un CPF (11 dígitos), `LEGAL_PERSON` para un CNPJ (14). El plugin convierte este tipo en el número que se transmite a JD, así que equivocarse registra a la parte como el tipo de persona equivocado.
    * **`externalId` tiene que ser el alias de la cuenta en el ledger** (`@payer`), y el nombre del campo lo oculta. El CRM lo describe como un identificador externo de correlación, lo que se lee como opcional. Para este riel no lo es: es donde el plugin lee qué cuenta del libro pertenece al titular. Un titular sin `externalId` produce una cuenta que se resuelve, se ve completa y falla en todos los pagos.
    * **`addresses.primary.city` es obligatorio para los QR codes y Pix Automático.** Es la ciudad del receptor que se imprime en el código, y el plugin se niega a generar un QR sin ella, responde `422 PIX-0033` y apunta a `payee.city`. Ninguna ruta de pago lee el campo, y por eso su ausencia pasa inadvertida hasta que alguien genera un QR.

    <Warning>
      Un titular admite exactamente una cuenta. `externalId` es un solo valor por titular, así que dar dos cuentas al mismo titular hace que la segunda mueva dinero en la primera. Para una segunda cuenta, crea un segundo titular.
    </Warning>
  </Step>

  <Step title="Vincular el titular a la cuenta">
    El titular y la cuenta del ledger ya existen, y todavía nada los une. Este paso es la unión, y es como el plugin encuentra **dónde acreditar** un Pix entrante.

    ```bash theme={null}
    curl -s -X POST "$CRM_URL/v1/holders/$CRM_HOLDER_ID/aliases" \
      -H "Authorization: Bearer $CRM_BEARER_TOKEN" -H 'Content-Type: application/json' \
      -H "X-Organization-Id: $MIDAZ_ORGANIZATION_ID" -d '{
      "ledgerId":  "'"$MIDAZ_LEDGER_ID"'",
      "accountId": "'"$MIDAZ_ACCOUNT_ID"'",
      "bankingDetails": {
        "branch":      "0001",
        "account":     "1234567",
        "type":        "CACC",
        "openingDate": "2020-01-02",
        "bankId":      "12345678"
      }
    }'
    ```

    | Campo                   | Qué es                                                                                   | Si falta                                                                                                                                |
    | ----------------------- | ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
    | `ledgerId`, `accountId` | el libro y la cuenta de los pasos anteriores                                             | el vínculo no referencia nada                                                                                                           |
    | `branch`, `account`     | número de sucursal y de cuenta — las coordenadas con las que se resuelve un Pix entrante | el crédito no encuentra destino                                                                                                         |
    | `type`                  | `CACC` corriente, `SLRY` salario, `SVGS` ahorro, `TRAN` pago                             | el plugin compara este tipo con el del pago y rechaza con `400 PIX-0019` cuando divergen                                                |
    | `openingDate`           | la **fecha** de apertura de la cuenta, no un instante                                    | `400 PIX-0061`, que dice que no se pudo determinar la fecha de apertura, en la verificación de claves y en la apertura de reclamaciones |
    | `bankId`                | el ISPB de 8 dígitos de tu propia institución                                            | mira la advertencia de abajo                                                                                                            |

    <Warning>
      Nunca envíes `bankId` como cadena vacía. Una cadena vacía es un valor: registra "esta cuenta pertenece a la institución cuyo ISPB está vacío", lo que es peor que no decir nada. Si todavía no tienes el ISPB, omite el campo.

      Las cuentas con `bankId` completado pagan; las cuentas creadas sin él respondieron un error de servidor en el cash-out. Ese síntoma está medido, pero el mecanismo no está confirmado. Complétalo — es tu propio ISPB, no cuesta nada, y la alternativa es depurar un error que no nombra nada.
    </Warning>

    Verifica qué quedó registrado, y filtra siempre. Una consulta sin filtro devuelve el primer vínculo de la organización, que es como una verificación termina aprobando la cuenta de otra persona:

    ```bash theme={null}
    curl -s -G "$CRM_URL/v1/aliases" \
      -H "Authorization: Bearer $CRM_BEARER_TOKEN" -H "X-Organization-Id: $MIDAZ_ORGANIZATION_ID" \
      --data-urlencode 'document=12345678909' \
      --data-urlencode 'banking_details_branch=0001' \
      --data-urlencode 'banking_details_account=1234567' | jq
    ```
  </Step>

  <Step title="Fondear las cuentas">
    Una cuenta nueva tiene cero, y no puedes pagar desde una cuenta vacía. El crédito de apertura viene de `@external/BRL`, creada junto con el activo en el paso 2.

    ```bash theme={null}
    curl -s -X POST "$MIDAZ_URL_TRANSACTION/v1/organizations/$MIDAZ_ORGANIZATION_ID/ledgers/$MIDAZ_LEDGER_ID/transactions/json" "${MIDAZ_HEADERS[@]}" -d '{
      "description": "opening balance",
      "send": {
        "asset": "BRL",
        "value": "100.00",
        "source":     { "from": [ { "accountAlias": "@external/BRL",
                                    "amount": { "asset": "BRL", "value": "100.00" } } ] },
        "distribute": { "to":   [ { "accountAlias": "@payer",
                                    "amount": { "asset": "BRL", "value": "100.00" } } ] }
      }
    }'
    ```

    <Warning>
      Dos cuerpos idénticos son un solo asiento. Midaz colapsa la repetición: el segundo `POST` responde `201` con el id del primer asiento, y nada se mueve. Una recarga que "funcionó" y no cambió el saldo es esto. Cambia la `description` en cada crédito.
    </Warning>

    `@external/BRL` se puede nombrar en el cuerpo pero nunca en una ruta. El alias contiene una barra y la ruta del ledger no la decodifica, así que leer su saldo por ruta responde `404` o un `200` vacío. Registrar asientos desde ella es normal; leerla de esa forma no lo es.
  </Step>

  <Step title="Crear las veinte rutas contables">
    Cada ruta de dinero de este riel tiene su propio par de rutas de operación de Midaz — una pata de crédito y una pata de débito. Diez perfiles, dos patas cada uno, así que veinte rutas. El paso de configuración de más abajo guarda sus UUID.

    ```bash theme={null}
    # a CREDIT leg -> operationType "destination" (money ARRIVES)
    curl -s -X POST "$MIDAZ_URL_ONBOARDING/v1/organizations/$MIDAZ_ORGANIZATION_ID/ledgers/$MIDAZ_LEDGER_ID/operation-routes" "${MIDAZ_HEADERS[@]}" -d '{
      "title": "pix-jd out credit",
      "description": "Pix JD accounting route",
      "operationType": "destination"
    }'

    # a DEBIT leg -> operationType "source" (money LEAVES)
    curl -s -X POST "$MIDAZ_URL_ONBOARDING/v1/organizations/$MIDAZ_ORGANIZATION_ID/ledgers/$MIDAZ_LEDGER_ID/operation-routes" "${MIDAZ_HEADERS[@]}" -d '{
      "title": "pix-jd out debit",
      "description": "Pix JD accounting route",
      "operationType": "source"
    }'
    ```

    Cada llamada devuelve `{"id": "..."}`, y ese id es lo que guardan las claves de enrutamiento de abajo.

    <Warning>
      La dirección es invertible y nada te avisa. Un débito **sale** del pagador, así que es la pata `source`; un crédito **llega**, así que es `destination`. Invertidas, cada asiento sigue respondiendo éxito — en la dirección equivocada. Ningún código de estado informa esto.
    </Warning>

    Busca una ruta por título antes de crearla. Dos rutas con el mismo título hacen que la búsqueda siguiente tome cualquiera de las dos, así que si aprovisionas el mismo libro más de una vez, lista primero.
  </Step>
</Steps>

## Configura el riel

La configuración viva del plugin vive en su **systemplane**: valores que escribes por la API de administración y que toman efecto sin un redespliegue ni un reinicio. Cada escritura es un `PUT` a `/system/<namespace>/<key>` con un cuerpo `{"value": ...}`, y `204 No Content` es el éxito — el plano no devuelve cuerpo en una escritura.

El tenant viene del bearer validado, nunca de la URL ni del cuerpo.

<Note>
  Pregúntale al plugin qué espera en lugar de confiar en una copia. `GET /system/-/catalog` lista todas las claves con su tipo y descripción, y `GET /system/-/catalog/<namespace>/<key>` describe una. El catálogo es la fuente de verdad; esta página es una copia de él, y las copias envejecen.
</Note>

<Steps>
  <Step title="Escribir el vínculo de integración con JD — tu ISPB">
    **Qué es.** El ISPB es el número de 8 dígitos que identifica a tu institución en el Banco Central. Es el "quién soy" que va en cada mensaje Pix, y el plugin no puede firmar nada como tuyo sin él.

    **Por qué no lo adivinarías.** El ISPB no es una variable de despliegue. Viene de la clave de systemplane `tenancy/jd_integration_binding`, y no hay fallback de entorno. Mientras la clave esté vacía el plugin arranca, responde a su sonda de salud, se ve sano — y rechaza **todos** los pagos en **todas** las rutas de dinero.

    **Qué se rompe sin él.** `409 PIX-0092`, *"Tenant Pix integration not provisioned"*. Una batería de punta a punta juntó 86 rechazos por este único valor vacío; el código siguiente más frecuente en la misma corrida apareció 6 veces. El texto de la respuesta te pide que contactes a soporte y no nombra la clave, así que el código es lo que buscas.

    <Warning>
      Reiniciar no ayuda. Este no es un valor que se lee en el arranque — el plugin lee la clave en cada llamada, así que reiniciar un despliegue que no tiene ISPB devuelve un despliegue que sigue sin ISPB. Escribir la clave sí ayuda, con la aplicación en ejecución: la solicitud siguiente pasa, sin redespliegue.
    </Warning>

    **La trampa: el valor es una cadena que contiene JSON.** El cuerpo siempre es `{"value": ...}`, y aquí `value` no es un objeto. Es una **cadena** cuyo contenido es un documento JSON. Así es como se guarda, con las comillas internas escapadas:

    ```text theme={null}
    "{\"ispb\":\"12345678\",\"organizationId\":\"...\",\"ledgerId\":\"...\"}"
    ```

    | Forma                                                  | Resultado |
    | ------------------------------------------------------ | --------- |
    | `{"value":"{\"ispb\":\"12345678\",...}"}` — una cadena | correcto  |
    | `{"value":{"ispb":"12345678",...}}` — un objeto        | rechazado |

    Deja que `jq` haga el escapado:

    ```bash theme={null}
    # Replace all three with your own values before you run this.
    INSTITUTION_ISPB="12345678"                                    # 8 digits, your institution's ISPB
    MIDAZ_ORGANIZATION_ID="3fa85f64-5717-4562-b3fc-2c963f66afa6"   # same value as the deployment variable
    MIDAZ_LEDGER_ID="9c858901-8a57-4791-81fe-4a34d4dd8ab5"         # same value as the deployment variable

    # 1) build the document
    BINDING_DOCUMENT="$(jq -nc \
            --arg ispb   "$INSTITUTION_ISPB" \
            --arg orgId  "$MIDAZ_ORGANIZATION_ID" \
            --arg ledgerId "$MIDAZ_LEDGER_ID" \
            '{ispb:$ispb, organizationId:$orgId, ledgerId:$ledgerId}')"

    # 2) wrap the document as a STRING inside {"value": ...} and write it
    curl -s -o /dev/null -w '%{http_code}\n' -X PUT \
      -H "Authorization: Bearer $PIX_JD_BEARER_TOKEN" -H 'Content-Type: application/json' \
      -d "$(jq -nc --arg document "$BINDING_DOCUMENT" '{value:$document}')" \
      "$PIX_JD_BASE_URL/system/tenancy/jd_integration_binding"
    ```

    | Campo            | Regla                                                                 | Obligatorio al escribir | Se lee en tiempo de ejecución               |
    | ---------------- | --------------------------------------------------------------------- | ----------------------- | ------------------------------------------- |
    | `ispb`           | exactamente 8 dígitos, `0`–`9`, sin máscara ni espacios               | sí                      | sí                                          |
    | `organizationId` | un UUID válido y distinto de cero (el UUID de todos ceros se rechaza) | sí                      | no — el runtime usa `MIDAZ_ORGANIZATION_ID` |
    | `ledgerId`       | un UUID válido y distinto de cero                                     | sí                      | no — el runtime usa `MIDAZ_LEDGER_ID`       |

    <Note>
      Los tres campos son obligatorios al escribir, y esa es la parte sorprendente. El despliegue no lee `organizationId` ni `ledgerId` de esta clave — el libro en el que registra sigue viniendo de `MIDAZ_ORGANIZATION_ID` y `MIDAZ_LEDGER_ID`. El validador de escritura pide los tres de todos modos. Completa los dos UUID con los mismos valores que llevan esas variables.

      Dos fuentes de verdad para el mismo hecho podrían discrepar sin que nadie lo note, y por eso la respuesta en tiempo de ejecución se queda con los valores del despliegue.
    </Note>

    <Warning>
      La decodificación es estricta: un campo desconocido se rechaza, no se ignora, y tumba toda la escritura. Eso incluye el campo retirado `routeProfiles` — las rutas contables se mudaron a las claves `routing.*` de abajo — así que un documento copiado de una configuración vieja no entra. Todo lo que venga después del primer documento JSON también se rechaza.
    </Warning>

    **Cómo verificar que quedó aplicado.** Vuelve a leer la clave. No guarda ningún secreto, así que el valor vuelve en claro:

    ```bash theme={null}
    curl -s -H "Authorization: Bearer $PIX_JD_BEARER_TOKEN" \
      "$PIX_JD_BASE_URL/system/tenancy/jd_integration_binding" | jq
    ```

    ```json theme={null}
    {
      "namespace": "tenancy",
      "key": "jd_integration_binding",
      "value": "{\"ispb\":\"12345678\",\"organizationId\":\"...\",\"ledgerId\":\"...\"}"
    }
    ```

    Si `value` vuelve como un objeto en lugar de una cadena entre comillas, escribiste la forma equivocada. Si vuelve como `""`, la escritura no ocurrió — revisa el código de estado del `PUT`. La confirmación real es de comportamiento: las rutas de dinero que respondían `409 PIX-0092` dejan de responderlo.

    Un ISPB malformado no se puede guardar por esta ruta. El validador de escritura es el mismo decodificador que usa la lectura, así que un ISPB de 7 dígitos o un UUID roto se rechaza en el acto con `400 validation_error` en lugar de descubrirse en el primer pago.

    <Note>
      Por eso no te vas a encontrar con `409 PIX-0121` — *"Tenant Pix integration ISPB invalid"* — mientras sigues esta página. Es el código de un vínculo que **sí** está aprovisionado y cuyo `ispb` no tiene 8 dígitos, y esta ruta no puede crear ese estado. Aparece solo cuando un valor llegó a la clave por otra vía: una escritura directa a la base de datos del plugin, o una escritura hecha antes de que existiera el validador. Está documentado porque, si alguna vez lo ves, su mensaje es el que nombra tanto la clave como el campo a corregir.
    </Note>

    <Warning>
      Un `400` aquí no es un problema de permisos. Esa lectura ya costó tiempo: tres escenarios de prueba leyeron este mismo `400` como "mi bearer no tiene permiso de administrador". El permiso faltante es `403`. Este `400` significa que el valor que enviaste está mal. La respuesta no dice qué campo, así que revisa primero los 8 dígitos — es el error más común.
    </Warning>

    | Respuesta              | Qué significa                                                             |
    | ---------------------- | ------------------------------------------------------------------------- |
    | `204`                  | escrito                                                                   |
    | `400 validation_error` | el **valor** fue rechazado por su formato — no es un problema de permisos |
    | `400 unknown_key`      | el nombre de la clave o del namespace está mal                            |
    | `401`                  | el bearer no autenticó                                                    |
    | `403`                  | autenticado, pero sin `systemplane:write`                                 |
    | `404`                  | el grupo `/system` no está montado — `SYSTEMPLANE_ENABLED` es false       |
    | `503`                  | el plano de configuración no está disponible                              |

    La cadena vacía se acepta, y es el centinela de "no aprovisionado". Escribirla devuelve el despliegue a rechazar con `409 PIX-0092`, así que no lo hagas en un entorno que está pagando.
  </Step>

  <Step title="Escribir las veinte claves de enrutamiento">
    **Qué es.** Para cada ruta de dinero, el par de UUID de las rutas de operación de Midaz creadas arriba. Las veinte claves viven en el namespace `tenant_policy`, todas guardan una cadena y todas guardan un UUID de ruta que ya existe en Midaz.

    ```
    tenant_policy/routing.<profile>.operation_credit_route
    tenant_policy/routing.<profile>.operation_debit_route
    ```

    | Perfil                    | Cuándo se usa                                      |
    | ------------------------- | -------------------------------------------------- |
    | `out`                     | un pago enviado                                    |
    | `out_reversal`            | la reversión de un pago enviado                    |
    | `in`                      | un crédito recibido                                |
    | `in_qrcode`               | un crédito recibido a través de un QR code         |
    | `intra_psp`               | un pago entre dos cuentas de tu propia institución |
    | `intra_psp_reversal`      | la reversión de ese pago                           |
    | `med_credit`, `med_debit` | las dos patas de una devolución MED (fraude)       |
    | `pixautomatico_debit`     | el débito de Pix Automático                        |
    | `pixautomatico_reversal`  | la reversión de Pix Automático                     |

    **Qué se rompe sin esto.** Una pata ausente no degrada el flujo, lo rechaza: la ruta de dinero correspondiente falla cerrada con `409 PIX-0105`. Eso es deliberado — rechazar una transacción es mejor que registrarla contra una ruta indefinida. Si un flujo específico "no funciona" y los demás sí, este es el primer lugar donde mirar.

    ```bash theme={null}
    # OPERATION_ROUTES is the table YOU fill with the UUIDs the ledger step returned:
    # one line per profile and leg, "<profile> <leg> <operation route UUID>". Twenty
    # lines when every flow is provisioned. There is no magic function here.
    OPERATION_ROUTES="
    out                    credit 1f0a4c2e-5b91-4d77-9a10-6c3b8e2f0a01
    out                    debit  1f0a4c2e-5b91-4d77-9a10-6c3b8e2f0a02
    out_reversal           credit 1f0a4c2e-5b91-4d77-9a10-6c3b8e2f0a03
    out_reversal           debit  1f0a4c2e-5b91-4d77-9a10-6c3b8e2f0a04
    "

    operation_route_id() {
      printf '%s\n' "$OPERATION_ROUTES" | awk -v profile="$1" -v leg="$2" \
        '$1 == profile && $2 == leg { print $3 }'
    }

    for profile in out out_reversal in in_qrcode intra_psp intra_psp_reversal \
                   med_credit med_debit pixautomatico_debit pixautomatico_reversal; do
      for leg in credit debit; do
        uuid="$(operation_route_id "$profile" "$leg")"
        if [ -z "$uuid" ]; then
          echo "MISSING the UUID for $profile.$leg — that flow will refuse with PIX-0105" >&2
          continue
        fi
        curl -s -o /dev/null -w "$profile/$leg -> %{http_code}\n" -X PUT \
          -H "Authorization: Bearer $PIX_JD_BEARER_TOKEN" -H 'Content-Type: application/json' \
          -d "{\"value\":\"$uuid\"}" \
          "$PIX_JD_BASE_URL/system/tenant_policy/routing.$profile.operation_${leg}_route"
      done
    done
    ```

    **Cómo verificar.** Cada escritura responde `204`. Un perfil que tu producto nunca ejercita puede quedar vacío — ese flujo entonces rechaza, que es lo que quieres en lugar de un asiento en una ruta indefinida.

    <Warning>
      Una cadena vacía es el centinela de "todavía no aprovisionado" y se acepta. El UUID de todos ceros se **rechaza**: se parsea sin problema pero nunca es un identificador real de Midaz. Un script que llena las rutas sin usar con ceros como un "vacío seguro" pasa cualquier verificación de formato ingenua y esta lo rechaza. Usa la cadena vacía.
    </Warning>
  </Step>

  <Step title="Definir el activo de registro y la cuenta de compensación">
    **Qué es.** El activo en el que se registra el dinero, y la cuenta externa que representa al mundo fuera de tu institución.

    **Dónde van los valores depende del modo de despliegue**, y aquí es donde un `204` puede confundirte:

    El activo de registro viene de `MIDAZ_ASSET_ID` y la cuenta de compensación de `MIDAZ_EXTERNAL_ID` — las dos variables de despliegue.

    <Warning>
      Las dos claves de systemplane `tenant_policy/midaz.asset_id` y `tenant_policy/midaz.external_id` existen, aceptan una escritura y responden `204` — y nada las lee: el plugin resuelve el activo y la cuenta de compensación desde las variables de despliegue. Puedes escribir ambas, obtener `204` en las dos y aun así tener la ruta de dinero rechazando, porque las variables siguen vacías. Un `204` aquí no confirma que el valor se vaya a usar.
    </Warning>

    **Los dos nombres mienten sobre su forma.** A pesar del `_ID`:

    | Valor                     | Qué espera                                 |
    | ------------------------- | ------------------------------------------ |
    | el activo de registro     | el **código** del activo: `BRL`            |
    | la cuenta de compensación | el **alias** de la cuenta: `@external/BRL` |

    Un UUID en cualquiera de los dos responde `PIX-4011` y no nombra ninguna cuenta.

    **Qué se rompe sin esto.** `409 PIX-0106`, *"Tenant ledger configuration missing"*. La respuesta nombra las dos mitades y los dos lugares donde definirlas, así que te dice cuál te falta. Es un código distinto de `PIX-0105`: un tenant puede tener las veinte patas de ruta correctas y aun así rechazar todos los asientos porque el activo o la cuenta de compensación están sin definir.
  </Step>

  <Step title="Definir la ventana diaria">
    Los dos extremos de la ventana en la que se contabilizan los límites de transacción. Ambos son enteros de 0 a 23 — horas de reloj, no timestamps — y ambos son claves de systemplane en **los dos** modos de despliegue.

    ```bash theme={null}
    PUT() { curl -s -o /dev/null -w "$1 -> %{http_code}\n" -X PUT \
              -H "Authorization: Bearer $PIX_JD_BEARER_TOKEN" -H 'Content-Type: application/json' \
              -d "$2" "$PIX_JD_BASE_URL/system/$1"; }

    PUT tenant_policy/transaction_limits.daily_period_init '{"value":6}'
    PUT tenant_policy/transaction_limits.daily_period_end  '{"value":20}'
    ```

    Los valores de arriba son un ejemplo; usa los tuyos. Lo que no cambia es el tipo JSON: un entero, sin comillas. Un valor fuera del rango responde `400` en lugar de recortarse en silencio al límite.
  </Step>

  <Step title="Declarar si este despliegue aloja participantes indirectos">
    `plugin-br-pix-jd.indirects/enabled` declara lo que el tenant **es**. Un participante directo simple lo define en `false`.

    ```bash theme={null}
    curl -s -o /dev/null -w '%{http_code}\n' -X PUT \
      -H "Authorization: Bearer $PIX_JD_BEARER_TOKEN" -H 'Content-Type: application/json' \
      -d '{"value":false}' \
      "$PIX_JD_BASE_URL/system/plugin-br-pix-jd.indirects/enabled"
    ```

    Es un booleano JSON, sin comillas: `{"value":"true"}` responde `400`. Si este despliegue liquida Pix por cuenta de otras instituciones, defínelo en `true` y sigue [Alojar participantes indirectos](/es/interfaces/pix-jd/hosting-indirect-participants) — hay un valor más para aprovisionar antes de poder registrar a alguien, y sin él todos los registros se rechazan.
  </Step>

  <Step title="Materializar los límites de transacción con un pago pequeño">
    Este es el paso menos adivinable de la página, porque el producto no ofrece forma de crear lo que necesita.

    **Qué esperas.** Aprovisionas una cuenta y lees su límite disponible.

    **Qué pasa.** `GET /v1/limits/available` responde `404 PIX-0063`, *"The specified transaction limit was not found in the system. Please verify the identifier and try again."*, y `GET /v1/limits` responde `{"data":[]}`. Parece una cuenta rota. No lo es: es el estado inicial normal de una cuenta que nunca transaccionó.

    `PIX-0063` nombra la cosa que se haya buscado, así que la [lista de errores](/es/reference/interfaces/pix-jd/pix-jd-error-list) imprime su forma genérica — *"The specified entity was not found in the system"* — y esta ruta completa `transaction limit`. Mismo código, mismo `404`.

    **Por qué no puedes arreglarlo creando algo.** No hay ruta de creación. `PATCH /v1/limits` actualiza una fila que ya tiene que existir. Las filas las materializa exactamente una cosa: la verificación previa de límites de un pago **saliente**. La primera vez que la cuenta envía un pago, el plugin nota que no tiene filas, crea los valores predeterminados, los vuelve a leer y sigue adelante.

    **Así que el paso es: envía un pago saliente pequeño.** Un centavo alcanza, y es lo que hace el aprovisionamiento automatizado.

    <Warning>
      Una cuenta sin filas de límite no es una cuenta sin límite. Si la creación automática no puede establecer los límites, el pago se **rechaza**, no se deja pasar.

      Y no intentes forzar las filas enviando un monto por encima del techo. La verificación de **saldo** corre antes del aplicador de límites, así que un monto grande se rechaza por fondos insuficientes y nunca se llega al aplicador — no aparece ninguna fila. Tiene que ser un pago común que cabe en el saldo.
    </Warning>

    **Cómo verificar.** `GET /v1/limits` pasa de `{"data":[]}` a una lista, y `/v1/limits/available` pasa de `404` a `200`.

    Este paso depende de que las claves de enrutamiento estén escritas primero: materializa las filas haciendo un pago real, y sin ruta el pago se rechaza con `PIX-0105`. Todo lo demás en esta página se puede hacer en cualquier orden.
  </Step>
</Steps>

## Alojar participantes indirectos

Un despliegue que liquida Pix por cuenta de otras instituciones agrega tres pasos de aprovisionamiento sobre la cadena de arriba: la clave de cifrado del secreto de entrega, la postura de alojamiento y el registro de cada participante indirecto. Están en [Alojar participantes indirectos](/es/interfaces/pix-jd/hosting-indirect-participants), y su numeración continúa la de esta página — pasos 7 a 9. Si tu despliegue liquida solo los Pix de sus propios clientes, tu aprovisionamiento termina aquí.

## Cómo se ve cada paso omitido

Cada rechazo de abajo es un `409` salvo el de los límites, y **ninguno se resuelve esperando**. Se resuelven aprovisionando el valor que falta.

| Falta                                                       | Respuesta      | Qué ves                                                                                                      |
| ----------------------------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------ |
| el vínculo del ISPB                                         | `409 PIX-0092` | todas las rutas de dinero rechazan. El texto te pide que contactes a soporte y no nombra ninguna clave       |
| una pata de ruta contable                                   | `409 PIX-0105` | solo rechazan los flujos que usan ese perfil; los demás siguen funcionando                                   |
| el activo de registro o la cuenta de compensación           | `409 PIX-0106` | todos los asientos se rechazan, y la respuesta nombra las dos mitades y dónde definirlas                     |
| las filas de límite de transacción                          | `404 PIX-0063` | *"The specified transaction limit was not found in the system. Please verify the identifier and try again."* |
| la clave de cifrado de entrega (solo en el flujo indirecto) | `409 PIX-0107` | todos los registros indirectos se rechazan; no se guardó nada                                                |

<Note>
  Que `PIX-0092` deje de aparecer no significa que los pagos pasen. Es la primera barrera, no la última: con el vínculo en su lugar, una ruta contable vacía sigue rechazando con `PIX-0105`, y un activo o una cuenta de compensación ausente sigue rechazando con `PIX-0106`. Tres códigos, tres causas, tres correcciones distintas.
</Note>

### No reintentes un `409`. Sí reintenta su hermano `503`

Esta es la distinción que te dice si el problema es tu configuración o la caída de alguien, y el estado la lleva.

Un `409` de arriba es **conocimiento positivo de ausencia**: el riel leyó tu configuración con éxito y no encontró nada ahí. Solo un operador puede aportar el valor, así que repetir la solicitud no puede cambiar la respuesta — un cliente que respeta la semántica de reintentos entraría en un bucle infinito contra una condición que nunca se resuelve sola. Cada una de esas respuestas nombra qué definir.

La mayoría de esas condiciones tiene un **hermano `503`** para el caso en que el riel no pudo leer la configuración en absoluto. No se estableció nada sobre lo que aprovisionaste, la respuesta nombra la dependencia que falla y reintentar es lo correcto.

| Tu configuración está incompleta — aprovisiona, no reintentes                          | La fuente no respondió — reintenta                                                     |
| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `409 PIX-0092` la integración Pix del tenant no está aprovisionada                     | `503 PIX-0051` no se pudo alcanzar la integración Pix del tenant                       |
| `409 PIX-0121` el vínculo existe y su `ispb` no tiene 8 dígitos                        | `503 PIX-0051` (el mismo hermano: no se pudo leer el vínculo)                          |
| `409 PIX-0106` el activo de registro o la cuenta de compensación no está aprovisionado | `503 PIX-0122` no se pudo leer la identidad del ledger desde el plano de configuración |
| `409 PIX-0107` la clave de cifrado del secreto de entrega no está aprovisionada        | `503 PIX-0123` no se pudo leer la clave desde su fuente de claves                      |

<Warning>
  **Las rutas contables son la excepción, y vale la pena saberlo.** `PIX-0105` no tiene hermano `503`: una pata de ruta ausente, vacía, malformada, el UUID de todos ceros, **o ilegible** responden todas ese mismo `409`. Así que a diferencia de todos los demás códigos de aquí, un `PIX-0105` por sí solo no separa "esta pata nunca se aprovisionó" de "el plano de configuración no respondió". Vuelve a leer las veinte patas antes de concluir que es una caída.
</Warning>

<Note>
  Las dos mitades de cada par rechazan la operación, y ninguna registra ni guarda nada. La diferencia está por completo en lo que debes hacer después, y por eso son códigos separados en lugar de un solo envelope que cubra ambos.

  `PIX-0121` es un caso especial de otra forma: no puedes provocarlo por la API de administración, porque el validador de escritura rechaza un `ispb` malformado antes de guardarlo. Aparece solo cuando un valor llegó a la clave por otra vía — mira el paso del vínculo de arriba.
</Note>

El catálogo completo, con el `detail` exacto que lleva cada código, es la [lista de errores de Pix JD](/es/reference/interfaces/pix-jd/pix-jd-error-list).

## Comprueba que la configuración está completa

Ejecuta estas comprobaciones en orden. Cada una falla por una razón distinta, y eso es lo que hace que valga la pena correr la secuencia en lugar de una sola prueba de humo.

1. **El ledger acepta una cuenta.** Crea una cuenta desechable y elimínala. Si el libro rechaza, el rechazo nombra el eslabón que falta. Sáltate esto y el mismo problema vuelve más tarde como "cuenta no encontrada" dentro de un flujo de pago, a tres capas de su causa. Nunca fondees la cuenta de prueba: Midaz se niega a eliminar una cuenta con saldo.
2. **El vínculo vuelve como una cadena entre comillas** que lleva tu ISPB, como se muestra arriba.
3. **La consulta de alias devuelve tu cuenta**, filtrada por documento, sucursal y número de cuenta.
4. **Una ruta de dinero deja de responder `409 PIX-0092`.** Es la misma llamada que ya hacías — no hace falta un arnés de pruebas.
5. **`GET /v1/limits` devuelve una lista** para una cuenta que envió su primer pago.

<Note>
  **Algunos pasos de aprovisionamiento no son tuyos.** La batería de punta a punta de Lerian establece cuatro cosas más antes de correr: un conjunto de infracciones MED, credenciales para su doble de pruebas de JD y dos archivos internos de contabilidad. Esos son fixtures de prueba sin equivalente en un despliegue real — en producción las infracciones llegan desde JD, y JD se autentica solo. No intentes construirlos.
</Note>

## A dónde ir después

<Columns cols={2}>
  <Card title="Variables de entorno" href="/es/interfaces/pix-jd/pix-jd-environment-variables">
    La configuración de este riel en el momento del despliegue: conectividad con JD, los endpoints del ledger y del CRM, el alojamiento de QR y los proveedores de notificaciones.
  </Card>

  <Card title="Participantes indirectos" href="/es/reference/interfaces/pix-jd/indirect-participants">
    Qué es una participación alojada, cómo llega a ella un crédito entrante, su ciclo de vida y todos los rechazos que puede responder.
  </Card>

  <Card title="Pix Directo vía JD" href="/es/interfaces/pix-jd/direct-pix-via-jd">
    Cómo aterrizan en Midaz los movimientos Pix liquidados, y cómo se correlacionan los dos sistemas.
  </Card>

  <Card title="Lista de errores de Pix JD" href="/es/reference/interfaces/pix-jd/pix-jd-error-list">
    Todos los códigos `PIX-NNNN`, su estado y el texto `detail` que lleva la respuesta.
  </Card>
</Columns>
