Create your Webhook Endpoint

The first thing you must do to use the Fintoc webhooks is to build your own webhook endpoint. Building a webhook endpoint is similar to building any endpoint for your application, but we think it's important to have a few considerations in mind before actually writing any code.

We also recommend that you read our guide about good practices for implementing webhooks.

Key considerations

For every event that happens on Fintoc, a JSON HTTP (POST) request will be sent to every registered webhook endpoint. From that request, you can parse the whole event detail using the JSON body.

Your webhook endpoint, then, must be able to receive POST request, handle the event information and then respond with any HTTP response code that belongs to the family of success codes (2XX).

Saving the received event

Every event sent by Fintoc as a webhook has a unique id. It is important to at least save the id corresponding to each event in your application's database. This way, you can ensure idempotency in the received events.

Returning 2XX quickly

To confirm that an event was successfully received, your application must respond with an 2XX HTTP status code. Any response with a status code other than that will be considered a failed attempt.

If Fintoc does not receive a 2XX status code, we will attempt to notify the event again. After multiple days and failed attempts, Fintoc will stop sending the event to that webhook endpoint.

It is very important that you respond with a 2XX status code as quickly as possible. Any complex logic on your application triggered by the reception of a webhook should be executed asynchronously. This way, we avoid notifying you multiple times about events that have already been received, but that failed to respond with a 2XX status code due to an HTTP timeout.

Testing the webhook

Due to notifications to the webhook endpoint occur only under specific circumstances, it is possible that some bugs of the reception of a webhook stay unnoticed until it's too late. That's why we always recommend that you test your endpoints when:

  • Creating a webhook endpoint
  • Registering a webhook endpoint
  • Performing any change that can affect the reception of the webhook

Sample code

require 'json'

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

  begin
    event = JSON.parse(payload)
  rescue JSON::ParserError => e
    # Invalid payload
    status 400
    return
  end
  
  # idempotency using sinatra-activerecord
  seen_event = WebhookEvent.find_by(fintoc_event_id: event['id'])
  if seen_event
    status 200
    return
  end
  
  # save new event for idempotency
  new_event = 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
import json
import os

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:
        return jsonify(success=False)
      
    # 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)
// 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) => {
  const event = request.body;
  
  // 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 ceredentilas changed event.
      break;
    case 'link.refresh_intent.succeeded':
      const linkId = event.data.id;
      // Then define and call a method to handle the link refreshed event.
      break;
    case 'account.refresh_intent.succeeded':
      const accountId = event.data.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'));