openapi: 3.0.1
info:
  title: Webhooks API
  description: >-
    # Tutorial


    Con el sistema de **webhooks**, puedes suscribirte a los eventos que se
    producen sobre la mayoría de recursos del sistema, de forma que puedes
    recibir notificaciones para que tu sistema reaccione como corresponda.


    La mayoría de recursos permiten suscribirse a los eventos de:


    - `created`

    - `updated`

    - `deleted`


    ## Recursos con soporte de Webhooks


    | Dominio     | Recurso               | Descripción                        |

    |-------------|------------------------|------------------------------------|

    | Contact     | Customer               | Clientes                          
    |

    | Contact     | Supplier               | Proveedores                       
    |

    | Warehouse   | Article                | Artículos                         
    |

    | Warehouse   | Warehouse              | Almacenes                         
    |

    | Purchase    | Order                  | Pedidos de proveedor              
    |

    | Purchase    | Delivery-note          | Albaranes de proveedor            
    |

    | Purchase    | Invoice                | Facturas de proveedor             
    |

    | Sale        | Quote                  | Presupuesto de cliente            
    |

    | Sale        | Order                  | Pedido de cliente                 
    |

    | Sale        | Delivery-note          | Albarán de cliente                
    |

    | Sale        | Invoice                | Factura de cliente                
    |

    | Sale        | Template-sale-invoice  | Plantilla de facturación periódica
    |

    | Accounting  | Account                | Cuentas contables                 
    |

    | Accounting  | Entry                  | Asientos contables                
    |


    ---


    ## Obtención del Api Key

    Para utilizar el sistema de webhooks, es necesario disponer de un Api Key
    desde el área del desarrollador.

    Los pasos para obtenerlo son los siguientes:

    1. Accede a tu cuenta de PRANA.

    2. Dirígete al área de "Configuración".

    3. Selecciona la opción "Desarrolladores".

    4. Haz clic en "Añadir API Key".

    5. Dale un nombre descriptivo a tu API Key y guarda los cambios.

    6. Copia el valor del API Key generado y guárdalo en un lugar seguro. Este
    valor tendrás que pasarlo como cabecera, en todas las peticiones que hagas
    contra el sistema.


    ![Añadir ApiKey](api-key.png)


    ---


    ## Recursos y eventos a los que puedes suscribirte


    Puedes obtener la lista completa de recursos y eventos a los que es posible
    suscribirse, de esta forma.


    **Request**


    ```http

    GET /event-type

    Content-Type: application/json

    x-api-key: <API_KEY>

    ```


    **Responses**


    **200 OK**


    Lista de tipos de evento disponibles en el sistema.


    ```json

    [
      {
        "appName": "PRANA",
        "eventDomain": "ACCOUNTING",
        "resource": "ACCOUNT",
        "eventName": "CREATED",
        "semanticName": "PRANA.ACCOUNTING.ACCOUNT.CREATED",
        "id": "c6e050ee-b2a8-4df9-bf80-7655be7afbdf",
        "active": true
      },
      {
        "appName": "PRANA",
        "eventDomain": "ACCOUNTING",
        "resource": "ACCOUNT",
        "eventName": "DELETED",
        "semanticName": "PRANA.ACCOUNTING.ACCOUNT.DELETED",
        "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
        "active": true
      }
    ]

    ```


    ## Subscribirse a eventos

    Para crear una nueva subscripción, debes hacer una petición POST indicando:

    * `callbackUrl`: URL de tu sistema donde deseas recibir las notificaciones

    * `secret`: Palabra clave que usaremos para firmar cada mensaje

    * `events`: Lista de cadenas con los nombres semánticos de los eventos a los
    que deseas suscribirte


    ```http

    POST /subscription

    Content-Type: application/json

    x-api-key: <API_KEY>

    {
        "callbackUrl": "https://url-de-tu-sistema.com/webhook",
        "secret": "<PALABRA_SECRETA>",
        "events": [
            "PRANA.CONTACT.CUSTOMER.CREATED",
            "PRANA.CONTACT.CUSTOMER.UPDATED",
            "PRANA.CONTACT.CUSTOMER.DELETED"
        ]
    }

    ```


    **200 OK**


    Obtendrás una respuesta con los datos de la suscripción creada.


    ```json

    {
      "subscriptionId": "f575bd47-826b-4ff8-9934-60656ee7d89f",
      "responseState": "SUCCESS",
      "errors": null
    }

    ```


    **handshake**


    Al crear una nueva suscripción, el sistema realizará una petición `POST` a
    la URL indicada en `callbackUrl`, para verificar que tu sistema está
    preparado para recibir las notificaciones. 

    El sistema esperará una respuesta con código `200 OK` y cuerpo vacío, para
    considerar que la verificación ha sido correcta.


    El mensaje de esta petición, contendrá la siguiente información.


    ```json

    {
      "subscriptionId": "f575bd47-826b-4ff8-9934-60656ee7d89f",
      "tenantInformation": {
        "tenantId": "999999",
        "userId": "aaaaaa999999"
      },
      "eventType": {
        "appName": "webhook",
        "eventDomain": "",
        "resource": "",
        "eventName": "handshake",
        "semanticName": "webhook...handshake",
        "id": "e53ae4e1-d970-43de-8bf5-f7be2954e46d",
        "active": true
      },
      "payload": "{\"Type\":\"Acknowledge\"}"
    }

    ```

    * `subscriptionId`: Identificador de la suscripción que acabas de crear.

    * `tenantInformation`: Información del inquilino y usuario asociado a la
    suscripción.

    * `eventType`: Tipo de evento, en este caso `handshake`.

    * `payload`: Contenido del mensaje, en este caso un JSON con el campo `Type`
    con valor `Acknowledge`.


    A partir de este momento, tu sistema estará preparado para recibir las
    notificaciones de los eventos a los que te has suscrito.


    ### Notificaciones de eventos

    Cada vez que se produzca un evento al que te has suscrito, el sistema
    realizará una petición `POST` a la URL indicada en `callbackUrl`. 

    Esta petición incluirá una cabecera `x-signature`, que contendrá una firma
    HMAC-SHA256 del cuerpo del mensaje, 

    utilizando la `secret` que proporcionaste al crear la suscripción. Esto te
    permitirá verificar la autenticidad del mensaje recibido.


    El cuerpo del mensaje tendrá la siguiente estructura:

    ```json

    {
      "subscriptionId": "f575bd47-826b-4ff8-9934-60656ee7d89f",
      "tenantInformation": {
        "tenantId": "999999",
        "userId": "aaaaaa999999"
      },
      "eventType": {
        "appName": "PRANA",
        "eventDomain": "CONTACT",
        "resource": "CUSTOMER",
        "eventName": "UPDATED",
        "semanticName": "PRANA.CONTACT.CUSTOMER.UPDATED"
      },
      "payload": "{\"EntityId\":\"369986\"}"
    }

    ```


    En el elemento `payload`, encontrarás la información específica del evento.
    En este caso, 

    el campo `EntityId` indica el identificador del cliente que ha sido
    actualizado. Con esta información, 

    podrás enviar una petición a la API para obtener los detalles del cliente
    actualizado.


    ## Consulta tus suscripciones

    En cualquier momento, puedes consultar las suscripciones que has creado
    realizando una petición `GET` a la siguiente URL:


    ```http

    GET /subscription

    Content-Type: application/json

    x-api-key: <API_KEY>

    ```


    ```json

    [
      {
        "id": "f575bd47-826b-4ff8-9934-60656ee7d89f",
        "secret": "<PALABRA_SECRETA>",
        "callbackUrl": "https://url-de-tu-sistema.com/webhook",
        "status": "ACTIVE",
        "statusMessage": "",
        "webhooks": [
          {
            "id": "2a87d134-6458-4379-af71-1cdab4f77d01",
            "eventType": {
              "appName": "PRANA",
              "eventDomain": "CONTACT",
              "resource": "CUSTOMER",
              "eventName": "CREATED",
              "semanticName": "PRANA.CUSTOMER.CUSTOMER.CREATED"
            }
          },
          {
            "id": "9562db1d-b3a5-43f3-b0d7-daebed0b7c06",
            "eventType": {
              "appName": "PRANA",
              "eventDomain": "CONTACT",
              "resource": "CUSTOMER",
              "eventName": "UPDATED",
              "semanticName": "PRANA.CUSTOMER.CUSTOMER.UPDATED"
            }
          },
          {
            "id": "55f62ac0-07e3-4d87-bf4f-5ecc55b47f89",
            "eventType": {
              "appName": "PRANA",
              "eventDomain": "CONTACT",
              "resource": "CUSTOMER",
              "eventName": "DELETED",
              "semanticName": "PRANA.CUSTOMER.CUSTOMER.DELETED"
            }
          }
        ]
      }
    ]

    ```


    ## Elimina una suscripción

    Si deseas eliminar una suscripción, puedes hacerlo realizando una petición
    `DELETE` de la siguiente forma:


    ```http

    DELETE /subscription/{subscriptionId}

    Content-Type: application/json

    x-api-key: <API_KEY>

    ```


    Donde `subscriptionId` es el identificador de la suscripción que deseas
    eliminar.


    En este caso, la respuesta del sistema será un código 200 OK, indicando que
    la suscripción ha sido eliminada correctamente.


    ## Actualiza una suscripción

    Si necesitas actualizar una suscripción existente, puedes hacerlo mediante
    una petición `PUT`. 

    La estructura de la petición es similar a la de creación, pero debes incluir
    el `subscriptionId` de la suscripción que deseas actualizar.


    Los valores que puedes actualizar son:

    * `callbackUrl`: URL de tu sistema donde deseas recibir las notificaciones

    * `secret`: Palabra clave que usaremos para firmar cada mensaje

    * `status`: Estado de la suscripción. Puede ser `ACTIVE` o `INACTIVE`


    ```http

    PUT /subscription/{subscriptionId}

    Content-Type: application/json

    x-api-key: <API_KEY>

    {
        "callbackUrl": "https://url-de-tu-sistema.com/webhook",
        "secret": "<PALABRA_SECRETA>",
        "status": "ACTIVE"
    }

    ```


    ## Firma y verificación de mensajes

    Cada notificación enviada a tu sistema incluirá una cabecera `x-signature`,
    que contiene una firma HMAC-SHA256 del cuerpo del mensaje. 

    Esta firma se genera utilizando la `secret` que proporcionaste al crear la
    suscripción.


    ```java

    import javax.crypto.Mac;

    import javax.crypto.SecretKey;

    import javax.crypto.spec.SecretKeySpec;

    import java.nio.charset.StandardCharsets;

    import java.security.InvalidKeyException;

    import java.security.NoSuchAlgorithmException;

    import java.util.Base64;


    public final class SignatureUtils {
        private static final String ALGORITHM = "HmacSHA256";

        private SignatureUtils() {
            throw new IllegalStateException("Utility class");
        }

        public static String generateSignature(String secret, String payload) {
            try {
                SecretKey secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), ALGORITHM);

                Mac mac = Mac.getInstance(ALGORITHM);
                mac.init(secretKey);

                return Base64.getEncoder().encodeToString(
                        mac.doFinal(payload.getBytes(StandardCharsets.UTF_8))
                );
            } catch (NoSuchAlgorithmException ex) {
                throw new IllegalStateException("Algorithm " + ALGORITHM + " not available", ex);
            } catch (InvalidKeyException ex) {
                throw new IllegalStateException("Invalid secret key for signature generation", ex);
            }
        }
    }

    ```
  version: 1.0.0
  contact:
    name: GoCloud Team
    email: support@gocloud.com
servers:
  - url: >-
      https://ca-prana-prod-weu-webhooks.whitesky-98ecba0e.westeurope.azurecontainerapps.io
    description: Generated server url
security:
  - ApiKeyAuth: []
tags:
  - name: Subscriptions
    description: Endpoints for subscription management
paths:
  /subscription/{id}:
    put:
      tags:
        - Subscriptions
      summary: Update a subscription
      description: Updates the data of an existing subscription
      operationId: updateWebhook
      parameters:
        - name: id
          in: path
          description: Subscription ID
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubscriptionUpdateWriteDto'
      responses:
        '200':
          description: Subscription updated successfully
        '400':
          description: Invalid input data
        '401':
          description: Unauthorized - Invalid API Key
        '403':
          description: Forbidden - No permission to update this subscription
        '404':
          description: Subscription not found
      security:
        - ApiKeyAuth: []
    delete:
      tags:
        - Subscriptions
      summary: Delete a subscription
      description: Deletes an existing subscription by its ID
      operationId: deleteWebhook
      parameters:
        - name: id
          in: path
          description: Subscription ID
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Subscription deleted successfully
        '401':
          description: Unauthorized - Invalid API Key
        '403':
          description: Forbidden - No permission to delete this subscription
        '404':
          description: Subscription not found
      security:
        - ApiKeyAuth: []
  /subscription:
    get:
      tags:
        - Subscriptions
      summary: Get all subscriptions
      description: >-
        Retrieves the list of subscriptions associated with the authenticated
        API Key
      operationId: getByApiKey
      responses:
        '200':
          description: Subscription list retrieved successfully
          content:
            application/json: {}
        '401':
          description: Unauthorized - Invalid API Key
          content:
            '*/*':
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/SubscriptionListReadDto'
      security:
        - ApiKeyAuth: []
    post:
      tags:
        - Subscriptions
      summary: Create a new subscription
      description: Creates a new subscription to webhook events
      operationId: subscribe
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubscriptionWriteDto'
      responses:
        '201':
          description: Subscription created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SubscriptionResponseReadDto'
        '400':
          description: Invalid input data
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/SubscriptionResponseReadDto'
        '401':
          description: Unauthorized - Invalid API Key
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/SubscriptionResponseReadDto'
      security:
        - ApiKeyAuth: []
components:
  schemas:
    SubscriptionUpdateWriteDto:
      type: object
      description: Updated subscription data
      properties:
        callbackUrl:
          type: string
          minLength: 0
          maxLength: 255
        secret:
          type: string
          minLength: 0
          maxLength: 255
        status:
          type: string
          enum:
            - PENDING
            - ACTIVE
            - INACTIVE
            - FAILED
    SubscriptionWriteDto:
      type: object
      description: Subscription data
      required:
        - callbackUrl
        - events
        - secret
      properties:
        callbackUrl:
          type: string
          minLength: 0
          maxLength: 255
        secret:
          type: string
          minLength: 0
          maxLength: 255
        events:
          type: array
          items:
            type: string
    SubscriptionResponseReadDto:
      type: object
      properties:
        subscriptionId:
          type: string
        responseState:
          type: string
          enum:
            - SUCCESS
            - FAILED
            - LOCKED
        errors:
          type: array
          items:
            type: string
    EventTypeListReadDto:
      type: object
      properties:
        appName:
          type: string
        eventDomain:
          type: string
        resource:
          type: string
        eventName:
          type: string
        semanticName:
          type: string
    SubscriptionListReadDto:
      type: object
      properties:
        id:
          type: string
        secret:
          type: string
        callbackUrl:
          type: string
        status:
          type: string
        statusMessage:
          type: string
        webhooks:
          type: array
          items:
            $ref: '#/components/schemas/WebhookSubscriptionListReadDto'
    WebhookSubscriptionListReadDto:
      type: object
      properties:
        id:
          type: string
        eventType:
          $ref: '#/components/schemas/EventTypeListReadDto'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      name: X-API-KEY
      in: header
