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

# Create your webhook endpoint

Create a webhook endpoint to receive and process event notifications from Fintoc. Before you write code, account for how the endpoint stores events, acknowledges delivery, and handles errors.

Read the [webhook best practices guide](/v2023-11-15/guides/resources/webhooks-walkthrough/webhooks-good-practices) before implementing your endpoint.

# Key considerations

Fintoc sends a JSON `POST` request to every registered webhook endpoint when an event occurs. Parse the event details from the request body.

Your webhook endpoint must accept `POST` requests, process the event data, and return an HTTP status code in the [`2xx` success range](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#2xx_success).

## Save the received event

Each webhook event from Fintoc has a unique `id`. Save at least the event `id` in your application's database so that you can process duplicate deliveries idempotently.

## Return a `2xx` response before processing

Your endpoint must return a `2xx` HTTP status code to acknowledge receipt. Fintoc treats any other status code as a failed delivery.

If Fintoc does not receive a `2xx` status code, Fintoc retries the event. Fintoc stops retrying after the final delivery attempt.

Return a `2xx` status code before running complex logic. Process other webhook logic asynchronously. This pattern prevents HTTP timeouts from triggering retries after your endpoint receives the event.

## Test the webhook

Webhook notifications occur only under specific conditions, so bugs in webhook handling can go unnoticed. Test your endpoint when you:

* Create a webhook endpoint
* Register a webhook endpoint
* Make a change that can affect webhook reception

# Sample code

**Webhook handler**

```python theme={null}
import json

from flask import Flask, jsonify, request

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    event = None
    payload = request.data
    try:
        event = json.loads(payload)
    except (json.JSONDecodeError, TypeError):
        return jsonify(success=False), 400
      
    # Add idempotency using the ORM being used by your app.

    # Handle the event
    if event and event['type'] == 'link.credentials_changed':
        link_id = event['data']['id']
        # Then define and call a method to handle the credentials changed event.
    elif event and event['type'] == 'link.refresh_intent.succeeded':
        link_id = event['data']['refreshed_object_id']
        # Then define and call a method to handle the link refreshed event.
    elif event and event['type'] == 'account.refresh_intent.succeeded':
        account_id = event['data']['refreshed_object_id']
        # Then define and call a method to handle the account refreshed event.
    # ... handle other event types
    else:
        # Unexpected event type
        print('Unhandled event type {}'.format(event['type']))
    return jsonify(success=True)
```

**Webhook handler**

```ruby theme={null}
require 'json'

# Using Sinatra
post '/webhook' do
  payload = request.body.read
  event = nil

  begin
    event = JSON.parse(payload)
  rescue JSON::ParserError
    # Invalid payload
    status 400
    return
  end
  
  # Add idempotency using Sinatra Active Record.
  seen_event = WebhookEvent.find_by(fintoc_event_id: event['id'])
  if seen_event
    status 200
    return
  end
  
  # Save the event for idempotency.
  WebhookEvent.create!(
    fintoc_event_id: event['id'],
    type: event['type'],
    data: event['data']
  )

  # Handle the event
  case event['type']
  when 'link.credentials_changed'
    link_id = event['data']['id']
    # Then define and call a method to handle the event.
  when 'link.refresh_intent.succeeded'
    link_id = event['data']['refreshed_object_id']
    # Then define and call a method to handle the link refreshed event.
  when 'account.refresh_intent.succeeded'
    account_id = event['data']['refreshed_object_id']
    # Then define and call a method to handle the account refreshed event.
  # ... handle other event types
  else
    # Unexpected event type
    puts "Unhandled event type: #{event['type']}"
  end

  status 200
end
```

**Webhook handler**

```javascript theme={null}
// This example uses Express to receive webhooks
const app = require('express')();

// Use body-parser to retrieve the raw body as a buffer
const bodyParser = require('body-parser');

app.post('/webhook', bodyParser.raw({ type: 'application/json' }), (request, response) => {
  let event;

  try {
    event = JSON.parse(request.body);
  } catch (error) {
    return response.status(400).json({ received: false });
  }
  
  // Add idempotency using the ORM being used by your app.

  // Handle the event
  switch (event.type) {
    case 'link.credentials_changed': {
      const linkId = event.data.id;
      // Then define and call a method to handle the credentials changed event.
      break;
    }
    case 'link.refresh_intent.succeeded': {
      const linkId = event.data.refreshed_object_id;
      // Then define and call a method to handle the link refreshed event.
      break;
    }
    case 'account.refresh_intent.succeeded': {
      const accountId = event.data.refreshed_object_id;
      // Then define and call a method to handle the account refreshed event.
      break;
    }
    // ... handle other event types
    default:
      // Unexpected event type
      console.log(`Unhandled event type ${event.type}`);
  }

  // Return a response to acknowledge receipt of the event
  response.json({ received: true });
});

app.listen(8000, () => console.log('Running on port 8000'));
```
