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

# NetSuite

> Guía de configuración para administradores de NetSuite — integración Payana

<Tabs>
  <Tab title="Español">
    Guía paso a paso para que los administradores de cuenta NetSuite configuren y obtengan todas las credenciales y permisos necesarios para la integración con Payana.

    ***

    ## 1. Obtener el Account ID

    1. Ve a **Setup > Company > Company Information**.
    2. Localiza el campo **Account ID** (ej. `1234567` o `1234567_SB1` en sandboxes).
    3. Copia y guarda este valor.

    <Tip>
      Si el Account ID contiene un guion bajo (ej. `_SB1`, `_RP`), indica un entorno Sandbox o Release Preview. Confirma el tipo de entorno al compartir.
    </Tip>

    ***

    ## 2. Habilitar Token-Based Authentication (TBA)

    1. Ve a **Setup > Company > Enable Features**.
    2. Abre la subpestaña **SuiteCloud**.
    3. En **Manage Authentication**, marca **Token-Based Authentication**.
    4. Haz clic en **Save**.

    ***

    ## 3. Crear el Integration Record (Consumer Key y Secret)

    1. Ve a **Setup > Integration > Manage Integrations > New**.
    2. Completa los campos:
       * **Name:** `Payana Integration` (u otro nombre descriptivo).
       * **State:** Enabled.
    3. En la subpestaña **Authentication**:
       * Marca **Token-Based Authentication**.
       * Desmarca **Authorization Code Grant** (no es necesario para servidor a servidor).
    4. Haz clic en **Save**.
    5. **Copia de inmediato** los valores mostrados:
       * **Consumer Key**
       * **Consumer Secret**

    <Warning>
      El Consumer Secret solo se muestra una vez al crear el registro. Si se pierde, debes crear un nuevo Integration Record.
    </Warning>

    ***

    ## 4. Crear el rol "Conexión Payana"

    ### 4.1 Crear el rol

    1. Ve a **Setup > Users/Roles > Manage Roles > New**.
    2. Configura:
       * **Name:** `Conexión Payana`.
       * **Centre Type:** **Classic Centre** (es crítico; ver nota abajo).
    3. En la subpestaña **Authentication**:
       * Marca **Web Services Only Role**.
    4. Haz clic en **Save** (agregarás permisos en los siguientes pasos).

    <Note>
      **¿Por qué Classic Centre?**\
      Los roles de NetSuite usan el Centre Type para determinar qué módulos y tipos de registro puede ver un usuario. Centros funcionales como *Accounting Centre* o *Employee Centre* restringen el acceso a ciertos registros y endpoints REST aunque el permiso figure como "Full". **Classic Centre** es el único Centre Type que da acceso sin restricciones a todos los módulos vía REST/SOAP, necesario para una integración servidor a servidor. Usar otro centro es una causa frecuente de `401 Invalid login` o respuestas API vacías difíciles de diagnosticar.
    </Note>

    ### 4.2 Permisos de Setup

    En la subpestaña **Permissions > Setup** agrega:

    | Permiso                    | Nivel    | Motivo                                                                        |
    | -------------------------- | -------- | ----------------------------------------------------------------------------- |
    | Log in using Access Tokens | **Full** | Requerido para TBA: permite autenticación con access tokens.                  |
    | REST Web Services          | **Full** | Da acceso a los endpoints REST usados por Payana.                             |
    | User Access Tokens         | **Full** | Permite crear y gestionar tokens asociados a este rol.                        |
    | Accounting Lists           | **Full** | Permite leer la tabla **PaymentMethod** (nombre del método de pago en pagos). |
    | SuiteAnalytics Workbook    | **Full** |                                                                               |

    ### 4.2 Permisos de Reportes

    En la subpestaña **Permissions > Reports** agrega:

    | Permiso                 | Nivel    | Motivo                                         |
    | :---------------------- | :------- | :--------------------------------------------- |
    | SuiteAnalytics Workbook | **Full** | Permite ejecutar consultas en la base de datos |

    ### 4.4 Permisos de transacciones

    En la subpestaña **Permissions > Transactions** agrega:

    #### Nivel 1 — Mínimo requerido (operaciones AP básicas)

    | Permiso                          | Nivel    | Motivo                                                                           |
    | -------------------------------- | -------- | -------------------------------------------------------------------------------- |
    | Bills (Vendor Bill)              | **Full** | Payana crea y actualiza facturas de proveedor (AP) en NetSuite.                  |
    | Purchase Orders (Purchase Order) | **Full** | Necesario para leer y referenciar POs al vincularlos a bills.                    |
    | Find Transaction                 | **Full** | Necesario para buscar transacciones existentes (bills, POs) y evitar duplicados. |

    #### Nivel 2 — Requerido para flujo AP completo

    | Permiso              | Nivel    | Motivo                                                                              |
    | -------------------- | -------- | ----------------------------------------------------------------------------------- |
    | Bill Purchase Orders | **Full** | Necesario para vincular una factura de proveedor a su orden de compra.              |
    | Enter Vendor Credits | **Full** | Permite crear notas de crédito cuando el proveedor emite reembolso total o parcial. |
    | Pay Bills            | **Full** | Necesario si Payana gestiona o registra pagos contra facturas de proveedor.         |

    ### 4.5 Permisos de listas

    En la subpestaña **Permissions > Lists** agrega:

    #### Nivel 1 — Mínimo requerido

    | Permiso        | Nivel    | Motivo                                                                                 |
    | -------------- | -------- | -------------------------------------------------------------------------------------- |
    | Vendors        | **Full** | Payana debe leer y resolver proveedores al crear bills.                                |
    | Subsidiaries   | **Full** | Necesario para asignar bills y POs a la subsidiaria correcta en cuentas multi-entidad. |
    | Accounts       | **Full** | Necesario para asignar cuentas contables (gastos, AP) en líneas de bills.              |
    | Perform Search | **Full** | Habilita SuiteQL y saved searches para el endpoint REST de consultas.                  |

    #### Nivel 2 — Requerido para flujo AP completo

    | Permiso     | Nivel    | Motivo                                                                                              |
    | ----------- | -------- | --------------------------------------------------------------------------------------------------- |
    | Items       | **Full** | Necesario para resolver ítems en líneas de bills (inventario y no inventario).                      |
    | Companies   | **Full** | Necesario para consultar entidades (los vendors son un subtipo de company) al crear bills.          |
    | Currency    | **Full** | Necesario para bills multi-moneda (tasas y códigos de moneda).                                      |
    | Departments | **Full** | Centros de costo (cuentas analíticas). La integración consulta la tabla **Department** vía SuiteQL. |
    | Locations   | **Full** | Bodegas/almacenes. La integración consulta la tabla **Location** vía SuiteQL.                       |

    ### 4.6 Permisos por tabla (SuiteQL)

    La integración usa **SuiteQL** para leer datos. Cada tabla requiere el permiso indicado en NetSuite. La siguiente lista coincide con las entidades a las que accede el procesador (equivalente funcional a la integración con Odoo).

    | Tabla SuiteQL       | Uso en la integración Payana                                                                | Permiso NetSuite que la habilita                                                          |
    | ------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
    | **Vendor**          | Proveedores (sincronización de suppliers, datos de beneficiarios en payables y pagos).      | Lists – Vendors                                                                           |
    | **Item**            | Productos e ítems (inventario y no inventario).                                             | Lists – Items                                                                             |
    | **Account**         | Cuentas contables (libros, métodos de pago, cuentas de gasto en líneas de PO y bills).      | Lists – Accounts                                                                          |
    | **Department**      | Centros de costo (cost centers).                                                            | Lists – Departments                                                                       |
    | **Location**        | Bodegas/almacenes (warehouses).                                                             | Lists – Locations                                                                         |
    | **Transaction**     | Facturas de proveedor (VendBill), órdenes de compra (PurchOrd), pagos (VendPymt, VendCred). | Transactions – Find Transaction + Bills, Purchase Orders, Pay Bills, Enter Vendor Credits |
    | **TransactionLine** | Líneas de PO, líneas de pago aplicadas a bills.                                             | Transactions – Find Transaction                                                           |
    | **PaymentMethod**   | Nombre del método de pago en pagos (opcional; si falta, se usa valor por defecto).          | Setup – Accounting Lists                                                                  |
    | **Subsidiary**      | Multi-entidad (OneWorld).                                                                   | Lists – Subsidiaries                                                                      |

    <Note>
      **Perform Search** (Lists) debe estar en **Full** para que las consultas SuiteQL funcionen. **Find Transaction** (Transactions) permite consultar la tabla **Transaction** y **TransactionLine** para los tipos de transacción permitidos (Bills, Purchase Orders, etc.).
    </Note>

    ### 4.6 Acceso a subsidiarias

    1. En el mismo rol, ve a la subpestaña **Subsidiaries** (solo si la cuenta usa OneWorld).
    2. Elige **All** o las subsidiarias que gestionará la integración.
    3. Haz clic en **Save**.

    ***

    ## 5. Asignar el rol a un usuario

    1. Ve a **Setup > Users/Roles > Manage Users** y selecciona el usuario que tendrá el token (puede ser un usuario dedicado a la integración).
    2. En la subpestaña **Access** > **Roles**, agrega el rol **Conexión Payana**.
    3. Haz clic en **Save**.

    <Tip>
      Es buena práctica usar un usuario dedicado (ej. `payana-integration@tuempresa.com`) en lugar de una cuenta personal, para evitar invalidar el token si alguien deja la empresa.
    </Tip>

    ***

    ## 6. Generar Access Tokens (Token ID y Token Secret)

    1. Ve a **Setup > Users/Roles > Access Tokens > New**.
    2. Completa:
       * **Application Name:** Selecciona `Payana Integration` (el Integration Record del paso 3).
       * **User:** Selecciona el usuario del paso 5.
       * **Role:** Selecciona `Conexión Payana`.
    3. Haz clic en **Save**.
    4. **Copia de inmediato** los valores mostrados:
       * **Token ID**
       * **Token Secret**

    <Warning>
      El Token Secret solo se muestra una vez al crear el token. Si se pierde, debes revocar el token y crear uno nuevo.
    </Warning>

    ***

    ## 7. Resumen de valores para compartir con Payana

    | Campo            | Dónde obtenerlo                                     |
    | ---------------- | --------------------------------------------------- |
    | Account ID       | Setup > Company > Company Information               |
    | Environment Type | Production / Sandbox / Release Preview              |
    | Consumer Key     | Se muestra una vez al guardar el Integration Record |
    | Consumer Secret  | Se muestra una vez al guardar el Integration Record |
    | Token ID         | Se muestra una vez al guardar el Access Token       |
    | Token Secret     | Se muestra una vez al guardar el Access Token       |

    Comparte estos seis valores por un **canal seguro**

    ***

    <div>
      ## 8. Lista de verificación

      * [ ] Token-Based Authentication está habilitado en SuiteCloud.
      * [ ] Existe un Integration Record con TBA habilitado.
      * [ ] El rol "Conexión Payana" usa **Classic Centre** como Centre Type.
      * [ ] El rol tiene **Web Services Only** marcado en la subpestaña Authentication.
      * [ ] Permisos de Setup: **Log in using Access Tokens**, **REST Web Services**, **User Access Tokens**, **SuiteAnalytics Workbook**, y **Accounting Lists** en Full.
      * [ ] Permisos de transacciones (Nivel 1): **Bills**, **Purchase Orders** y **Find Transaction** en Full.
      * [ ] Permisos de transacciones (Nivel 2): **Bill Purchase Orders**, **Enter Vendor Credits** y **Pay Bills** en Full.
      * [ ] Permisos de listas (Nivel 1): **Vendors**, **Subsidiaries**, **Accounts** y **Perform Search** en Full.
      * [ ] Permisos de listas (Nivel 2): **Items**, **Companies**, **Currency**, **Departments** y **Locations** en Full.
      * [ ] Permisos por tabla SuiteQL (sección 4.5): acceso a Vendor, Item, Account, Department, Location, Transaction, TransactionLine y, si aplica, PaymentMethod.
      * [ ] El rol tiene configurado el acceso a subsidiarias (All o lista específica).
      * [ ] Un usuario de integración dedicado tiene asignado el rol.
      * [ ] Se ha creado el Access Token y se han copiado Token ID y Token Secret.
      * [ ] Las seis credenciales se han compartido con Payana de forma segura.
      * [ ] Se verificó el acceso con una solicitud de prueba (sección 9).
    </div>

    ***

    ## 9. Validar la conexión  (Opcional)

    Una vez configuradas las credenciales, verifica que la integración tiene acceso correcto a NetSuite.

    ### Con Postman (recomendado)

    Postman maneja la firma OAuth 1.0 automáticamente, lo que lo hace la forma más sencilla de probar.

    <Steps>
      <Step title="Crear una solicitud POST">
        Usa la siguiente URL, reemplazando `ACCOUNT_ID` por tu Account ID en minúsculas (usa guiones en lugar de guiones bajos para sandboxes):

        ```text theme={null}
        https://ACCOUNT_ID.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql
        ```

        Ejemplo producción: `https://1234567.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql` Ejemplo sandbox: `https://1234567-sb1.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql`
      </Step>

      <Step title="Configurar autenticación OAuth 1.0">
        En la pestaña **Authorization**:

        | Campo            | Valor                                                  |
        | ---------------- | ------------------------------------------------------ |
        | Type             | OAuth 1.0                                              |
        | Signature Method | HMAC-SHA256                                            |
        | Consumer Key     | Tu Consumer Key                                        |
        | Consumer Secret  | Tu Consumer Secret                                     |
        | Access Token     | Tu Token ID                                            |
        | Token Secret     | Tu Token Secret                                        |
        | Realm            | Tu Account ID original (ej. `1234567` o `1234567_SB1`) |

        Marca la opción **Add params to header**.
      </Step>

      <Step title="Agregar headers">
        En la pestaña **Headers**:

        | Header         | Valor              |
        | -------------- | ------------------ |
        | `Content-Type` | `application/json` |
        | `Prefer`       | `transient`        |
      </Step>

      <Step title="Enviar la consulta de prueba">
        En **Body** selecciona **raw** > **JSON** y pega:

        ```json theme={null}
        { "q": "SELECT id, companyName FROM entity WHERE ROWNUM <= 1" }
        ```

        Haz clic en **Send**.
      </Step>
    </Steps>

    ### Respuesta esperada

    Una respuesta **200 OK** confirma que las credenciales y permisos están correctos:

    ```json theme={null}
    {
      "count": 1,
      "hasMore": false,
      "items": [
        {
          "id": "123",
          "companyname": "Example Vendor"
        }
      ],
      "offset": 0,
      "totalResults": 1
    }
    ```

    ### Errores comunes

    | Código | Significado                  | Qué revisar                                                                                              |
    | ------ | ---------------------------- | -------------------------------------------------------------------------------------------------------- |
    | `401`  | Credenciales inválidas       | Verifica que Consumer Key, Consumer Secret, Token ID y Token Secret sean correctos y no estén expirados. |
    | `403`  | Permisos insuficientes       | El rol no tiene los permisos necesarios. Revisa las secciones 4.2 a 4.5.                                 |
    | `404`  | Account ID o URL incorrectos | Confirma el Account ID y que el formato de URL corresponda al entorno.                                   |

    <AccordionGroup>
      <Accordion title="Alternativa: probar con cURL">
        Copia el siguiente script, reemplaza los valores de credenciales al inicio, y pégalo directamente en una terminal (macOS, Linux o Windows con WSL/Git Bash).

        Requiere `curl`, `openssl`, `python3` y `base64`.

        ```bash theme={null}
        # ── Credenciales (reemplaza estos valores) ──
        ACCOUNT_ID_ORIG="YOUR_ACCOUNT_ID"       # ej. 1234567 o 1234567_SB1
        CONSUMER_KEY="YOUR_CONSUMER_KEY"
        CONSUMER_SECRET="YOUR_CONSUMER_SECRET"
        TOKEN_ID="YOUR_TOKEN_ID"
        TOKEN_SECRET="YOUR_TOKEN_SECRET"
                
        # ── Derived values ──
        ACCOUNT_ID_URL=$(echo "$ACCOUNT_ID_ORIG" | tr '[:upper:]' '[:lower:]' | tr '_' '-')
        REALM="$ACCOUNT_ID_ORIG"
        URL="https://${ACCOUNT_ID_URL}.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql"

        # ── Query ──
        # Test de conexión:
        # QUERY="SELECT 1+1 AS result FROM DUAL"
        # Test de vendors:
        QUERY="SELECT id, entityid, companyname, email FROM vendor WHERE ROWNUM <= 10"

        # ── OAuth 1.0 signature ──
        TIMESTAMP=$(date +%s)
        NONCE=$(openssl rand -hex 16)

        ENCODED_URL=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${URL}', safe=''))")
        PARAMS="oauth_consumer_key=${CONSUMER_KEY}&oauth_nonce=${NONCE}&oauth_signature_method=HMAC-SHA256&oauth_timestamp=${TIMESTAMP}&oauth_token=${TOKEN_ID}&oauth_version=1.0"
        ENCODED_PARAMS=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${PARAMS}', safe=''))")

        SIGNATURE_BASE="POST&${ENCODED_URL}&${ENCODED_PARAMS}"
        SIGNING_KEY="${CONSUMER_SECRET}&${TOKEN_SECRET}"

        SIGNATURE=$(printf '%s' "$SIGNATURE_BASE" | openssl dgst -sha256 -hmac "$SIGNING_KEY" -binary | base64 | tr -d '\n')
        ENCODED_SIGNATURE=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${SIGNATURE}', safe=''))")

        AUTH_HEADER="OAuth oauth_consumer_key=\"${CONSUMER_KEY}\",oauth_token=\"${TOKEN_ID}\",oauth_signature_method=\"HMAC-SHA256\",oauth_timestamp=\"${TIMESTAMP}\",oauth_nonce=\"${NONCE}\",oauth_version=\"1.0\",oauth_signature=\"${ENCODED_SIGNATURE}\",realm=\"${REALM}\""

        # ── Execute request ──
        curl -s -X POST "$URL" \
          -H "Authorization: ${AUTH_HEADER}" \
          -H "Content-Type: application/json" \
          -H "Prefer: transient" \
          -H "accept-language: en" \
          -d "{\"q\":\"${QUERY}\"}" \
          | python3 -m json.tool

        ```
      </Accordion>

      <Accordion title="Consultas de validación adicionales">
        Una vez que la consulta básica funcione, verifica acceso a los tipos de registro específicos que usa la integración:

        ```sql theme={null}
        -- Facturas de proveedor
        SELECT id, tranId, entity FROM transaction WHERE type = 'VendBill' AND ROWNUM <= 1

        -- Órdenes de compra
        SELECT id, tranId, entity FROM transaction WHERE type = 'PurchOrd' AND ROWNUM <= 1

        -- Proveedores
        SELECT id, companyName FROM vendor WHERE ROWNUM <= 1

        -- Subsidiarias (solo OneWorld)
        SELECT id, name FROM subsidiary WHERE ROWNUM <= 1

        -- Centros de costo (Departments)
        SELECT id, fullname FROM department WHERE ROWNUM <= 1

        -- Bodegas (Locations)
        SELECT id, fullname FROM location WHERE ROWNUM <= 1
        ```

        Si todas las consultas retornan resultados (o un array `items` vacío con status `200`), las credenciales y permisos están correctamente configurados.
      </Accordion>
    </AccordionGroup>
  </Tab>

  <Tab title="English">
    Step-by-step guide for NetSuite account administrators to configure and obtain all the credentials and permissions required by the Payana integration.

    ***

    ## 1. Obtain the Account ID

    1. Navigate to **Setup > Company > Company Information**.
    2. Locate the **Account ID** field (e.g., `1234567` or `1234567_SB1` for sandboxes).
    3. Copy and save this value.

    <Tip>
      If the Account ID contains an underscore (e.g., `_SB1`, `_RP`), it indicates a Sandbox or Release Preview environment. Confirm the environment type when sharing.
    </Tip>

    ***

    ## 2. Enable Token-Based Authentication (TBA)

    1. Go to **Setup > Company > Enable Features**.
    2. Open the **SuiteCloud** subtab.
    3. Under **Manage Authentication**, check **Token-Based Authentication**.
    4. Click **Save**.

    ***

    ## 3. Create the Integration Record (Consumer Key & Secret)

    1. Navigate to **Setup > Integration > Manage Integrations > New**.
    2. Fill in the fields:
       * **Name:** `Payana Integration` (or a descriptive name).
       * **State:** Enabled.
    3. Under the **Authentication** subtab:
       * Check **Token-Based Authentication**.
       * Uncheck **Authorization Code Grant** (not needed for server-to-server).
    4. Click **Save**.
    5. **Immediately copy** the values shown:
       * **Consumer Key**
       * **Consumer Secret**

    <Warning>
      The Consumer Secret is displayed only once at creation time. If lost, you must create a new Integration Record.
    </Warning>

    ***

    ## 4. Create the "Conexión Payana" Role

    ### 4.1 Create the Role

    1. Go to **Setup > Users/Roles > Manage Roles > New**.
    2. Set the following fields:
       * **Name:** `Conexión Payana`.
       * **Centre Type:** **Classic Centre** — this is critical (see note below).
    3. Under the **Authentication** subtab:
       * Check **Web Services Only Role**.
    4. Click **Save** (you will add permissions in the next steps).

    <Note>
      **Why Classic Centre?**\
      NetSuite roles use a Centre Type to determine which UI modules and record types a user can access. Functional centres like *Accounting Centre* or *Employee Centre* silently restrict access to certain record types and REST endpoints — even if the permission appears as "Full" in the permissions list. **Classic Centre** is the only Centre Type that gives unrestricted access to all modules via REST/SOAP, which is required for a server-to-server integration. Using any other centre type is a common cause of `401 Invalid login` or empty API responses that are hard to diagnose.
    </Note>

    ### 4.2 Assign Setup Permissions

    Navigate to the **Permissions > Setup** subtab and add:

    | Permission                 | Level    | Why                                                                                     |
    | -------------------------- | -------- | --------------------------------------------------------------------------------------- |
    | Log in using Access Tokens | **Full** | Required for TBA: allows the role to authenticate via access tokens.                    |
    | REST Web Services          | **Full** | Grants access to the REST API endpoints used by Payana.                                 |
    | User Access Tokens         | **Full** | Allows creating and managing tokens tied to this role.                                  |
    | Accounting Lists           | **Full** | Optional: allows reading the **PaymentMethod** table (payment method name on payments). |
    | SuiteAnalytics Workbook    | **Full** |                                                                                         |

    ### 4.3 Assign Transaction Permissions

    Navigate to the **Permissions > Transactions** subtab and add:

    #### Tier 1 — Minimum required (core AP operations)

    | Permission                       | Level    | Why                                                                                      |
    | -------------------------------- | -------- | ---------------------------------------------------------------------------------------- |
    | Bills (Vendor Bill)              | **Full** | Payana creates and updates vendor bills (AP invoices) in NetSuite.                       |
    | Purchase Orders (Purchase Order) | **Full** | Needed to read and reference POs when linking them to bills.                             |
    | Find Transaction                 | **Full** | Required to search for existing transactions (bills, POs) and avoid creating duplicates. |

    #### Tier 2 — Required for full AP workflow

    | Permission           | Level    | Why                                                                                        |
    | -------------------- | -------- | ------------------------------------------------------------------------------------------ |
    | Bill Purchase Orders | **Full** | Needed to match (link) a vendor bill to its originating purchase order.                    |
    | Enter Vendor Credits | **Full** | Allows creating credit memos when a vendor issues a partial or full refund against a bill. |
    | Pay Bills            | **Full** | Required if Payana manages or records payment events against vendor bills.                 |

    ### 4.4 Assign List Permissions

    Navigate to the **Permissions > Lists** subtab and add:

    #### Tier 1 — Minimum required

    | Permission     | Level    | Why                                                                                  |
    | -------------- | -------- | ------------------------------------------------------------------------------------ |
    | Vendors        | **Full** | Payana must read and resolve vendor records when creating bills.                     |
    | Subsidiaries   | **Full** | Required to assign bills and POs to the correct subsidiary in multi-entity accounts. |
    | Accounts       | **Full** | Needed to assign GL accounts (expense accounts, AP accounts) to bill line items.     |
    | Perform Search | **Full** | Enables SuiteQL and saved searches — required for the REST query endpoint.           |

    #### Tier 2 — Required for full AP workflow

    | Permission  | Level    | Why                                                                                               |
    | ----------- | -------- | ------------------------------------------------------------------------------------------------- |
    | Items       | **Full** | Needed to resolve item records on bill lines (inventory and non-inventory).                       |
    | Companies   | **Full** | Required to look up entity records (vendors are a subtype of company) during bill creation.       |
    | Currency    | **Full** | Needed for multi-currency bills — allows reading and assigning exchange rates and currency codes. |
    | Departments | **Full** | Cost centers (analytic accounts). The integration queries the **Department** table via SuiteQL.   |
    | Locations   | **Full** | Warehouses. The integration queries the **Location** table via SuiteQL.                           |

    ### 4.5 Permissions by table (SuiteQL)

    The integration uses **SuiteQL** to read data. Each table requires the corresponding NetSuite permission. The list below matches the entities the processor accesses (same functional scope as the Odoo integration).

    | SuiteQL Table       | Use in Payana integration                                                           | NetSuite permission that enables it                                                       |
    | ------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
    | **Vendor**          | Vendors (supplier sync, beneficiary data on payables and payments).                 | Lists – Vendors                                                                           |
    | **Item**            | Products and items (inventory and non-inventory).                                   | Lists – Items                                                                             |
    | **Account**         | GL accounts (books, payment methods, expense accounts on PO and bill lines).        | Lists – Accounts                                                                          |
    | **Department**      | Cost centers.                                                                       | Lists – Departments                                                                       |
    | **Location**        | Warehouses.                                                                         | Lists – Locations                                                                         |
    | **Transaction**     | Vendor bills (VendBill), purchase orders (PurchOrd), payments (VendPymt, VendCred). | Transactions – Find Transaction + Bills, Purchase Orders, Pay Bills, Enter Vendor Credits |
    | **TransactionLine** | PO lines, payment lines applied to bills.                                           | Transactions – Find Transaction                                                           |
    | **PaymentMethod**   | Payment method name on payments (optional; default value used if missing).          | Setup – Accounting Lists                                                                  |
    | **Subsidiary**      | Multi-entity (OneWorld).                                                            | Lists – Subsidiaries                                                                      |

    <Note>
      **Perform Search** (Lists) must be **Full** for SuiteQL queries to work. **Find Transaction** (Transactions) allows querying the **Transaction** and **TransactionLine** tables for the transaction types you have permission for (Bills, Purchase Orders, etc.).
    </Note>

    ### 4.6 Configure Subsidiary Access

    1. In the same role record, go to the **Subsidiaries** subtab (only available if the account uses OneWorld).
    2. Select **All** or choose the specific subsidiaries the integration will manage.
    3. Click **Save**.

    ***

    ## 5. Assign the Role to a User

    1. Go to **Setup > Users/Roles > Manage Users** and select the user that will own the token (can be a dedicated integration user).
    2. Under the **Access** subtab > **Roles**, add the **Conexión Payana** role.
    3. Click **Save**.

    <Tip>
      Create a dedicated user (e.g., `payana-integration@yourcompany.com`) rather than using a personal account. This avoids token invalidation if an employee leaves.
    </Tip>

    ***

    ## 6. Generate Access Tokens (Token ID & Token Secret)

    1. Navigate to **Setup > Users/Roles > Access Tokens > New**.
    2. Fill in:
       * **Application Name:** Select `Payana Integration` (the Integration Record from Step 3).
       * **User:** Select the user from Step 5.
       * **Role:** Select `Conexión Payana`.
    3. Click **Save**.
    4. **Immediately copy** the values shown:
       * **Token ID**
       * **Token Secret**

    <Warning>
      The Token Secret is displayed only once at creation time. If lost, you must revoke the token and create a new one.
    </Warning>

    ***

    ## 7. Summary of Values to Share with Payana

    | Field            | Where to find it                          |
    | ---------------- | ----------------------------------------- |
    | Account ID       | Setup > Company > Company Information     |
    | Environment Type | Production / Sandbox / Release Preview    |
    | Consumer Key     | Shown once when saving Integration Record |
    | Consumer Secret  | Shown once when saving Integration Record |
    | Token ID         | Shown once when saving Access Token       |
    | Token Secret     | Shown once when saving Access Token       |

    Share these six values through a **secure channel**

    ***

    <div>
      ## 8. Verification Checklist

      * [ ] Token-Based Authentication is enabled in SuiteCloud features.
      * [ ] Integration Record exists with TBA enabled.
      * [ ] Role "Conexión Payana" uses **Classic Centre** as Centre Type.
      * [ ] Role has **Web Services Only** checked under the Authentication subtab.
      * [ ] Setup permissions: **Log in using Access Tokens**, **REST Web Services**, **User Access Tokens**, **SuiteAnalytics Workbook**, and **Accounting Lists** at Full.
      * [ ] Transaction permissions (Tier 1): **Bills**, **Purchase Orders**, and **Find Transaction** at Full.
      * [ ] Transaction permissions (Tier 2): **Bill Purchase Orders**, **Enter Vendor Credits**, and **Pay Bills** at Full.
      * [ ] List permissions (Tier 1): **Vendors**, **Subsidiaries**, **Accounts**, and **Perform Search** at Full.
      * [ ] List permissions (Tier 2): **Items**, **Companies**, **Currency**, **Departments**, and **Locations** at Full.
      * [ ] SuiteQL table permissions (Section 4.5): access to Vendor, Item, Account, Department, Location, Transaction, TransactionLine, and PaymentMethod if applicable.
      * [ ] Role has subsidiary access configured (All or specific list).
      * [ ] A dedicated integration user is assigned the role.
      * [ ] Access Token has been created and both Token ID / Token Secret have been copied.
      * [ ] All six credentials have been shared securely with Payana.
      * [ ] Connection validated with a test request (Section 9).
    </div>

    ***

    ## 9. Validate the Connection (Optional)

    Once all credentials are configured, verify that the integration has proper access to NetSuite.

    ### With Postman (recommended)

    Postman handles OAuth 1.0 signing automatically, making it the easiest way to test.

    <Steps>
      <Step title="Create a POST request">
        Use the following URL, replacing `ACCOUNT_ID` with your Account ID in lowercase (use hyphens instead of underscores for sandboxes):

        ```text theme={null}
        https://ACCOUNT_ID.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql
        ```

        Production example: `https://1234567.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql` Sandbox example: `https://1234567-sb1.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql`
      </Step>

      <Step title="Configure OAuth 1.0 authentication">
        In the **Authorization** tab:

        | Field            | Value                                                       |
        | ---------------- | ----------------------------------------------------------- |
        | Type             | OAuth 1.0                                                   |
        | Signature Method | HMAC-SHA256                                                 |
        | Consumer Key     | Your Consumer Key                                           |
        | Consumer Secret  | Your Consumer Secret                                        |
        | Access Token     | Your Token ID                                               |
        | Token Secret     | Your Token Secret                                           |
        | Realm            | Your original Account ID (e.g., `1234567` or `1234567_SB1`) |

        Check **Add params to header**.
      </Step>

      <Step title="Add headers">
        In the **Headers** tab:

        | Header         | Value              |
        | -------------- | ------------------ |
        | `Content-Type` | `application/json` |
        | `Prefer`       | `transient`        |
      </Step>

      <Step title="Send the test query">
        In **Body**, select **raw** > **JSON** and paste:

        ```json theme={null}
        { "q": "SELECT id, companyName FROM entity WHERE ROWNUM <= 1" }
        ```

        Click **Send**.
      </Step>
    </Steps>

    ### Expected Response

    A **200 OK** response confirms that the credentials and permissions are correctly configured:

    ```json theme={null}
    {
      "count": 1,
      "hasMore": false,
      "items": [
        {
          "id": "123",
          "companyname": "Example Vendor"
        }
      ],
      "offset": 0,
      "totalResults": 1
    }
    ```

    ### Common Errors

    | Code  | Meaning                  | What to check                                                                                 |
    | ----- | ------------------------ | --------------------------------------------------------------------------------------------- |
    | `401` | Invalid credentials      | Verify Consumer Key, Consumer Secret, Token ID, and Token Secret are correct and not expired. |
    | `403` | Insufficient permissions | The role is missing required permissions. Review Sections 4.2–4.5.                            |
    | `404` | Wrong Account ID or URL  | Confirm the Account ID and that the URL format matches the environment.                       |

    <AccordionGroup>
      <Accordion title="Alternative: test with cURL">
        Copy the following script, replace the credential values at the top, and paste it directly into a terminal (macOS, Linux, or Windows with WSL/Git Bash).

        Requires `curl`, `openssl`, `python3`, and `base64`.

        ```bash theme={null}
        # ── Credentials (replace these values) ──
        ACCOUNT_ID_ORIG="YOUR_ACCOUNT_ID"       # e.g. 1234567 or 1234567_SB1
        CONSUMER_KEY="YOUR_CONSUMER_KEY"
        CONSUMER_SECRET="YOUR_CONSUMER_SECRET"
        TOKEN_ID="YOUR_TOKEN_ID"
        TOKEN_SECRET="YOUR_TOKEN_SECRET"
              
        # ── Derived values ──
        ACCOUNT_ID_URL=$(echo "$ACCOUNT_ID_ORIG" | tr '[:upper:]' '[:lower:]' | tr '_' '-')
        REALM="$ACCOUNT_ID_ORIG"
        URL="https://${ACCOUNT_ID_URL}.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql"

        # ── Query ──
        # Conection test:
        # QUERY="SELECT 1+1 AS result FROM DUAL"
        # Query test:
        QUERY="SELECT id, entityid, companyname, email FROM vendor WHERE ROWNUM <= 10"

        # ── OAuth 1.0 signature ──
        TIMESTAMP=$(date +%s)
        NONCE=$(openssl rand -hex 16)

        ENCODED_URL=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${URL}', safe=''))")
        PARAMS="oauth_consumer_key=${CONSUMER_KEY}&oauth_nonce=${NONCE}&oauth_signature_method=HMAC-SHA256&oauth_timestamp=${TIMESTAMP}&oauth_token=${TOKEN_ID}&oauth_version=1.0"
        ENCODED_PARAMS=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${PARAMS}', safe=''))")

        SIGNATURE_BASE="POST&${ENCODED_URL}&${ENCODED_PARAMS}"
        SIGNING_KEY="${CONSUMER_SECRET}&${TOKEN_SECRET}"

        SIGNATURE=$(printf '%s' "$SIGNATURE_BASE" | openssl dgst -sha256 -hmac "$SIGNING_KEY" -binary | base64 | tr -d '\n')
        ENCODED_SIGNATURE=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${SIGNATURE}', safe=''))")

        AUTH_HEADER="OAuth oauth_consumer_key=\"${CONSUMER_KEY}\",oauth_token=\"${TOKEN_ID}\",oauth_signature_method=\"HMAC-SHA256\",oauth_timestamp=\"${TIMESTAMP}\",oauth_nonce=\"${NONCE}\",oauth_version=\"1.0\",oauth_signature=\"${ENCODED_SIGNATURE}\",realm=\"${REALM}\""

        # ── Execute request ──
        curl -s -X POST "$URL" \
        -H "Authorization: ${AUTH_HEADER}" \
        -H "Content-Type: application/json" \
        -H "Prefer: transient" \
        -H "accept-language: en" \
        -d "{\"q\":\"${QUERY}\"}" \
        | python3 -m json.tool
        ```
      </Accordion>

      <Accordion title="Additional validation queries">
        Once the basic query works, verify access to the specific record types the integration needs:

        ```sql theme={null}
        -- Vendor bills
        SELECT id, tranId, entity FROM transaction WHERE type = 'VendBill' AND ROWNUM <= 1

        -- Purchase orders
        SELECT id, tranId, entity FROM transaction WHERE type = 'PurchOrd' AND ROWNUM <= 1

        -- Vendors
        SELECT id, companyName FROM vendor WHERE ROWNUM <= 1

        -- Subsidiaries (OneWorld only)
        SELECT id, name FROM subsidiary WHERE ROWNUM <= 1

        -- Cost centers (Departments)
        SELECT id, fullname FROM department WHERE ROWNUM <= 1

        -- Warehouses (Locations)
        SELECT id, fullname FROM location WHERE ROWNUM <= 1
        ```

        If all queries return results (or an empty `items` array with a `200` status), the credentials and permissions are correctly configured.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
