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 keep a few considerations in mind before you write 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, Fintoc sends a POST request with a JSON body to every registered webhook endpoint. From that request, you can parse the whole event detail using the JSON body.
Your webhook endpoint must receive the POST request and parse the event. It then responds with any status code in the 2xx range.
Saving the received event
Every event sent by Fintoc as a webhook has a unique id. At a minimum, save the id corresponding to each event in your application's database. Saving the event id lets you ensure idempotency for received events.
Return a 2xx status code before the timeout
2xx status code before the timeoutTo confirm receipt of an event, respond with a 2xx HTTP status code. Fintoc treats any other status code as a failed attempt.
Respond with a 2xx status code before Fintoc's delivery timeout. Run any heavy logic your handler triggers asynchronously so the handler returns before the timeout.
Retries and failure handling
Fintoc treats a delivery as a failed attempt if your endpoint responds with any status code outside the 2xx range, or if the request times out.
When a delivery fails, Fintoc automatically retries it using exponential backoff, starting 3 seconds after the failed attempt. Fintoc retries an event up to 17 times before giving up.
If every attempt fails, Fintoc stops retrying that event. Keep in mind:
- Fintoc does not disable the webhook endpoint automatically, and does not notify you when it stops retrying an event.
- Fintoc does not support manual replay or a dead-letter mechanism to resend a failed event.
Because retries can deliver the same event more than once, your endpoint must be idempotent. See Avoid event duplication for how to handle duplicate events.
Testing the webhook
Fintoc sends webhook events only when the underlying action occurs, so a bug in your handler can go unnoticed until production traffic hits it. Test your endpoint whenever you create it, register it, or change anything that affects how it receives events.
For the step-by-step test procedure, including how to expose your local endpoint and send a test event, see Test your webhook endpoint.
Sample code
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:
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) => {
let event;
try {
event = JSON.parse(request.body);
} catch (err) {
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;
// Define and call a method to handle the credentials changed event.
break;
}
case 'link.refresh_intent.succeeded': {
const linkId = event.data.refreshed_object_id;
// Define and call a method to handle the link refreshed event.
break;
}
case 'account.refresh_intent.succeeded': {
const accountId = event.data.refreshed_object_id;
// 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'));