> ## 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

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](/docs/resources/webhooks-walkthrough/webhooks-good-practices) 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

To 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](/docs/resources/webhooks-walkthrough/webhooks-good-practices#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](/docs/resources/webhooks-walkthrough/webhooks-testing).

## Sample code

```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:
        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)
```

```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 (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'));
```
