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

# Webhooks

> Receive real-time updates about the status of transactions.

# Webhooks

The Webhook feature allows your system to receive real-time updates about the status of transactions. When a transaction status changes, Tumipay sends a POST request to the URL provided in the `ipn_url` field during the transaction creation.

## Webhook Payload

The webhook payload is a JSON object containing the following information:

<CodeGroup dropdown>
  ```json Webhook Payload theme={null}
  {
    "top_status": "APPROVED",
    "top_ticket": "49e3c70f-49d2-11ef-a534-02530a7dec0f",
    "top_reference": "ef3bc5cc-1a08-41c8-9e3b-449b95ac5eb6",
    "top_amount": 20000,
    "top_currency": "COP",
    "top_payment_method": "RECAUDO",
    "top_bank_name": "BANCO FALABELLA",
    "top_response_message": "Transacción exitosa",
    "top_user": {
      "legal_doc_type": "CC",
      "legal_doc": "879406421",
      "phone_code": "57",
      "phone_number": "3002134607",
      "email": "janejohnson@email.com",
      "full_name": "Jane Johnson"
    },
    "top_card": {
      "bank": "BANCO FALABELLA",
      "bin": "531122",
      "country": "Colombia",
      "lastFourDigits": "1974",
      "type": "credit",
      "brand": "Mastercard"
    },
    "top_user_email": "janejohnson@email.com",
    "top_user_phone": "3002134607",
    "top_extra_params1": ""
  }
  ```
</CodeGroup>

### Payload Properties

<Accordion title="Transaction Status Fields">
  <ResponseField name="top_status" type="string">
    The status of the transaction. Possible values:

    * `APPROVED`: The transaction was successfully completed
    * `REJECTED`: The transaction failed during the normal flow (e.g., declined by the bank)
    * `DECLINED`: The transaction was canceled due to internal rules, such as fraud detection, transactional limits, or merchant configurations
    * `PENDING`: The transaction is in progress and awaiting confirmation
    * `CREATED`: The transaction has been initiated but not yet processed
    * `EXPIRED`: The transaction has expired due to timeout
  </ResponseField>

  <ResponseField name="top_ticket" type="string">
    Unique identifier of the transaction
  </ResponseField>

  <ResponseField name="top_reference" type="string">
    Reference identifier provided by the merchant for the transaction
  </ResponseField>
</Accordion>

<Accordion title="Transaction Details Fields">
  <ResponseField name="top_amount" type="integer">
    Total amount of the transaction
  </ResponseField>

  <ResponseField name="top_currency" type="string">
    Currency of the transaction (e.g., `COP`)
  </ResponseField>

  <ResponseField name="top_payment_method" type="string">
    The payment method used (e.g., `RECAUDO`)
  </ResponseField>

  <ResponseField name="top_bank_name" type="string">
    Name of the bank involved in the transaction
  </ResponseField>

  <ResponseField name="top_response_message" type="string">
    Message describing the transaction's outcome
  </ResponseField>
</Accordion>

<Accordion title="Customer Information Fields">
  <ResponseField name="top_user" type="object">
    <Expandable title="User details">
      <ResponseField name="legal_doc_type" type="string">
        Type of the customer legal document (e.g., `CC`)
      </ResponseField>

      <ResponseField name="legal_doc" type="string">
        Customer legal document number
      </ResponseField>

      <ResponseField name="phone_code" type="string">
        Customer country phone code
      </ResponseField>

      <ResponseField name="phone_number" type="string">
        Customer phone number
      </ResponseField>

      <ResponseField name="email" type="string">
        Customer email address
      </ResponseField>

      <ResponseField name="full_name" type="string">
        Customer full name
      </ResponseField>
    </Expandable>
  </ResponseField>

  <ResponseField name="top_user_email" type="string">
    Customer email address (repeated for convenience)
  </ResponseField>

  <ResponseField name="top_user_phone" type="string">
    Customer phone number (repeated for convenience)
  </ResponseField>
</Accordion>

<Accordion title="Payment Card Fields">
  <ResponseField name="top_card" type="object">
    <Expandable title="Card details">
      <ResponseField name="bank" type="string">
        Name of the issuing bank
      </ResponseField>

      <ResponseField name="bin" type="string">
        Bank Identification Number (first 6 digits of the card)
      </ResponseField>

      <ResponseField name="country" type="string">
        Country of issuance
      </ResponseField>

      <ResponseField name="lastFourDigits" type="string">
        Last four digits of the card number
      </ResponseField>

      <ResponseField name="type" type="string">
        Card type (e.g., `credit`, `debit`)
      </ResponseField>

      <ResponseField name="brand" type="string">
        Card brand (e.g., `Visa`, `Mastercard`)
      </ResponseField>
    </Expandable>
  </ResponseField>

  <ResponseField name="top_extra_params1" type="string">
    Additional parameters
  </ResponseField>
</Accordion>

## Webhook Security

<Card title="Request Headers" icon="shield-halved">
  The webhook request includes the following headers for validation:

  <Properties>
    <Property name="x-trx-signature" type="string">
      A signature generated based on certain values in the payload
    </Property>

    <Property name="User-Agent" type="string">
      The user agent for the webhook is `Tumipay/1.1`
    </Property>
  </Properties>
</Card>

### Signature Generation

<Warning>
  Always validate the webhook signature to ensure that the request is legitimate and comes from Tumipay.
</Warning>

The signature is generated using the SHA256 algorithm by hashing a JSON object that includes the customer token, the transaction ticket, and the transaction reference.

<CodeGroup dropdown>
  ```json Signature Format theme={null}
  {
      "token": "<client_token>",
      "ticket": "<uuid_of_transaction>",
      "reference": "<transaction_reference>"
  }
  ```
</CodeGroup>

### Signature Verification Steps

<Steps>
  <Step title="Reconstruct the JSON object">
    Create a JSON string with your client token and the received transaction details
  </Step>

  <Step title="Apply SHA256 algorithm">
    Generate a signature by hashing the JSON object with SHA256
  </Step>

  <Step title="Compare signatures">
    Compare your generated signature with the one in the `x-trx-signature` header
  </Step>

  <Step title="Validate request">
    Only process the webhook if the signatures match
  </Step>
</Steps>

## Handling Webhook Events

<Tip>
  Your system should respond to webhook requests with a `200 OK` HTTP status code to confirm successful receipt.
</Tip>

### Retry Schedule

If Tumipay does not receive a successful response, it will retry sending the webhook according to this schedule:

<CardGroup cols={2}>
  <Card title="First Retry" icon="clock">
    5 minutes after the initial attempt
  </Card>

  <Card title="Second Retry" icon="clock">
    15 minutes after the first retry
  </Card>

  <Card title="Third Retry" icon="clock">
    30 minutes after the second retry
  </Card>

  <Card title="Fourth Retry" icon="clock">
    1 hour after the third retry
  </Card>

  <Card title="Fifth Retry" icon="clock">
    2 hours after the fourth retry
  </Card>
</CardGroup>

After these attempts, if no successful response is received, the webhook will be marked as failed and no further retries will be made.

### Example Use Case: Declined vs Rejected

<Tabs>
  <Tab title="DECLINED">
    Indicates the transaction was canceled internally by merchant-specific rules, such as:

    * Fraud detection triggers
    * Exceeding transactional limits
    * Additional configurations made by the merchant
  </Tab>

  <Tab title="REJECTED">
    Indicates the transaction was declined during the normal transaction process, such as:

    * Insufficient funds
    * Bank-specific rejections
  </Tab>
</Tabs>

### Example Use Case: Idempotent Processing

<Info>
  To prevent processing the same webhook event multiple times, implement idempotency checks in your webhook handler.
</Info>

<Steps>
  <Step title="Store processed transactions">
    Store a record of processed transactions using their `top_ticket` or `top_reference` as a unique key
  </Step>

  <Step title="Check for duplicates">
    When a webhook is received, check if the `top_ticket` already exists in your database
  </Step>

  <Step title="Handle accordingly">
    If the transaction exists, skip processing to avoid duplicates. Otherwise, proceed with handling the event and store the record
  </Step>
</Steps>

<Check>
  By following these practices, you can ensure a reliable and secure webhook implementation that effectively handles real-time transaction updates.
</Check>

### Handling Common Response Messages

<Card title="Common Rejection Messages" icon="triangle-exclamation">
  When receiving webhooks, be prepared to handle these common response messages for transactions with "REJECTED" status:
</Card>

<AccordionGroup>
  <Accordion title="Account Issues">
    * "Esta cuenta está inactiva/bloqueada" - Account is inactive/blocked
    * "La cuenta o Nit no corresponden" - Account or tax ID do not match
    * "El número de cuenta no existe" - Account number does not exist
    * "El número de cuenta no es válido" - Account number is invalid
    * "Cuenta no autorizada" - Unauthorized account
  </Accordion>

  <Accordion title="Document Issues">
    * "El Nit no es válido" - Invalid tax ID
  </Accordion>

  <Accordion title="Other Issues">
    * "El producto de destino no existe" - Destination product does not exist
    * "Excede el tope saldo de la cuenta destino" - Exceeds destination account balance limit
  </Accordion>
</AccordionGroup>

By following these practices, you can ensure a reliable and secure webhook implementation that effectively handles real-time transaction updates.
