Quiltt Logo

Webhooks

Webhooks enable you to react to changes in our systems without having to poll for data.

Quiltt will notify your system in real time when a specific event has happened. Example events are when a Profile is created, updated or deleted, or when a Connection is synced or enters an error state.

Link to this section#How we use Webhooks

We provide multiple event types you can subscribe to. When relevant events occur, Quiltt will send a notification to the subscribed endpoint via HTTPS. The notification contains a JSON payload with the event types and the events that have occurred. This allows you to execute actions in your system based on changes in Quiltt.

Link to this section#How to manage Webhooks

You can manage your Webhooks in the Quiltt Dashboard, or via the API, using the /webhooks/subscriptions endpoint.

Link to this section#Event Types

Below are some of the events you can subscribe to::

TypeDescription
profileAll Profile-related events.
profile.createdA Profile has been created.
connectionAll Connection-related events.
connection.synced.successfulA Connection has synced successfully.
connection.synced.errored.repairableA Connection has entered an error that must be resolved using the Reconnect flow
account.verifiedAn Account has been successfully verified and ACH numbers are now available via API.

Each event payload will return information about the relevant data model and associated Profile. For example, the connection.created event will include information about the created Connection, along with the associated Profile.

You can find the full list of event types and schemas in the Admin API Reference.

Link to this section#How to Subscribe to a Webhook via API

You can subscribe to events by sending a POST request to our webhooks subscriptions endpoint:

POSThttps://api.quiltt.io/v1/webhooks/subscriptions

Requests must include the following HTTP headers:

Authorization: Bearer <API_SECRET_KEY>
Content-Type: application/json

Requests must include a JSON body with the name of your subscription, a list of event types, and the target URL that should receive the webhooks.

{
  "name": "Connection Created",
  "targetUrl": "https://example.com/quiltt_webhook",
  "eventTypes": ["connection.created"]
}

Here is an example request that will create a subscription to account.verified events.

curl --request POST \
  --url 'https://api.quiltt.io/v1/webhooks/subscriptions' \
  --header 'Authorization: Bearer <API_SECRET_KEY>' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "name": "Account Verified",
    "targetUrl": "https://example.com/webhooks/quiltt",
    "eventTypes": ["account.verified"]
  }'

Successful responses will return a 201 HTTP response code.

Link to this section#How to Receive a Webhook

When subscribed events occur, Quiltt will send a POST request to your targetUrl, with a JSON payload.

Your endpoint should respond back with a 2xx HTTP response code to indicate that you've successfully processed the payload. In Production environments, Quiltt will retry sending the request several times until a 2xx response is received, using an exponential backoff between attempts.

The JSON payload will contain the relevant data about the event(s) that triggered the webhook.

Link to this section#Webhook Payload

The webhook payload includes a list of eventTypes and a list of events.

PropertyTypeDescription
environmentobjectrequiredInformation about the Environment.
eventTypesarray of stringsrequiredThe types of events included in the Payload.
eventsarray of objectsrequiredThe events included in this Payload.

Each event is structured as an object with an id, type, and a record object.

PropertyTypeDescription
idstringrequiredThe ID of the Event.
typestringrequiredThe type of Event that occurred.
profileobjectThe Profile associated with the Event.
recordobjectThe record associated with the Event.

See an example of the JSON payload below:

{
  "environment": {
    "id": "env_12uGLpOocVGvQYY9sFsOC6",
    "name": "Production",
    "mode": "PRODUCTION",
    "metadata": null
  },
  "eventTypes": ["connection.synced.successful"],
  "events": [
    {
      "id": "evt_12sDfIGkY96vVvNvTqNfn9",
      "type": "connection.synced.successful",
      "profile": {
        "id": "p_12uGLpPexyTkZCcJJntSjb",
        "uuid": "018a72f8-5434-7262-a029-a186fb0c5f33",
        "metadata": null
      },
      "record": {
        "id": "conn_12uGLpQUjSjdQqpIYoEVdQ",
        "status": "SYNCED",
        "metadata": null,
        "createdAt": "2023-09-08T04:06:32Z",
        "updatedAt": "2023-10-03T18:33:03Z"
      }
    }
  ]
}

Link to this section#Webhook Verification

Webhooks are provided with a timestamp and a HMAC256 Signature, with a per subscription key, to allow you to verify that each message is issued by Quiltt, is intended for you, is received unmodified, and is not being repeated.

To verify the message is valid:

  1. Ensure the Unix Epoch time Quiltt-Timestamp is current (within 5 minutes of current time in UTC)
  2. Validate Quiltt-Signature matches a Base64 encoded HMAC-SHA256 of version+timestamp+payload.
    • We are currently using version 1.

Link to this section#Sample Implementation

The following example shows how you could parse the received JSON payload and decide how to process the data of each event, based on its type. This example is using Ruby, but the logic will be similar in your language of choice.

require 'sinatra'
require 'json'

WEBHOOK_SECRET  = '<WEBHOOK_SUBSCRIPTION_SECRET>'
WEBHOOK_VERSION = 1
WEBHOOK_WINDOW  = 300 # Five minutes

# Using Sinatra
post '/quiltt_webhook' do

  # Ensure the Unix Epoch time `Quiltt-Timestamp` is current (within 5 minutes)
  if (timestamp = request['Quiltt-Timestamp']) < (Time.now.to_i - WEBHOOK_WINDOW)
    halt 204
  end

  # Validate `Quiltt-Signature` matches a Base64 encoded HMAC-SHA256 of version+timestamp+payload.
  payload   = request.body.read
  signature = OpenSSL::HMAC.base64digest('SHA256', WEBHOOK_SECRET, "#{WEBHOOK_VERSION}#{timestamp}#{payload}")
  if request['Quiltt-Signature'] != signature
    halt 204
  end

  # @note Only parse after payload is trusted by signature
  webhook = JSON.parse(payload)

  webhook['events'].each do |event|
    # Optional: Ensure Message is Unique
    # You may do this by either caching and blocking via event id. We only resend messages on failed delivery.

    case event['type']
    when 'connection.synced.successful'
      connection = event['record'] # contains the Connection data
      puts "Connection with ID #{connection['id']} has synced successfully!"
      # Call the Quiltt API to fetch the latest data on associated accounts or accounts.
    when 'account.verified'
      account = event['record'] # contains the Account data
      puts "Account with ID #{connection['id']} is ready for payment processing!"
      # Call the Quiltt API to fetch the ACH numbers to send to your payment provider
    else
      puts "Unhandled event #{event['type']}"
    end
  end

  status 204
end

Link to this section#Error Handling

In the event of a failed delivery, webhooks will automatically retry up to 3 times per attempted delivery, followed by up to 25 times redelivery attemps, with an exponential backoff between attempts. This is designed to handle both random network delivery errors and service outages on the receivers end.

Note that the redelivery behavior only enabled in production mode and will not function in non-production environments.

Link to this section#API Reference

See the Webhooks API Reference for full documentation of the subscription endpoint and the JSON payload we send to you.