# Webhooks

As a Jiko Partner API user, you may want to be made aware of events surrounding
your account, as they happen in real time. For this, we recommend setting up
subscriptions to Jiko Partner API webhooks.

## Available Webhook Subscriptions

To view available event types, make an authenticated request to
[List Event Types](/products/partner-api/reference/events/list_event_types_api_v1_events_types__get):


```bash
send_jiko_request "GET" "/api/v1/events/types/"
```

It should return a 200 OK response with an object that looks something like
this:


```json
{
  "offset":0,
  "count": 16,
  "items": [
    {
      "event":"application.approved",
      "object_type":"EventType"
    },
    {
      "event":"application.documents_needed",
      "object_type":"EventType"
    },
    ...
  ],
  "object_type":"List"
}
```

The list of available event types are found in your response object, by looping
through `items["event"]`.

To see example payloads for each event type, see the
[Trigger Webhook documentation](/products/partner-api/reference/sandbox/trigger-webhook).

The following event types are currently available:

1. `application.approved`
2. `application.documents_needed`
3. `application.manual_review`
4. `application.rejected`
5. `card.status.closed`
6. `card.status.frozen`
7. `card.status.locked`
8. `card.status.open`
9. `card.transaction.approved`
10. `card.transaction.rejected`
11. `transfers.ach.out.success`
12. `transfers.ach.out.rejected`
13. `transfers.ach.in.success`
14. `transfers.ach.in.rejected`
15. `transfers.wire.out.processing`
16. `transfers.wire.out.success`
17. `transfers.wire.out.rejected`
18. `transfers.on-us.processing`
19. `transfers.on-us.success`
20. `transfers.on-us.rejected`


## Creating a Subscription

Call Create Subscription with the following data in the request body:

1. list of event types to subscribe to
2. URL to invoke when an event is created
3. `shared_secret` of your choice (16 character minimum)
4. optional description of the subscription


Your request should look something like this:


```bash
body='{
 "description": "Webhook subscription for application statuses",
 "events": [
   "application.approved",
   "application.documents_needed",
   "application.manual_review",
   "application.rejected"
 ],
 "url": "https://example.com/webhooks/application-status",
 "shared_secret": "0123456789123456"
}';
send_jiko_request "POST" "/api/v1/subscriptions/" $body
```

If you get a 201 status code response then you're up and running and your
response should look something like this:


```json
{
  "description": "Webhook subscription for application statuses",
  "events": [
    "application.approved",
    "application.manual_review",
    "application.documents_needed",
    "application.rejected"
  ],
  "url": "https://example.com/webhooks/application-status",
  "id": "55b53873-3abf-4abd-971f-65331ce6dcc4",
  "time_created": "2022-08-09T17:30:52.646971+00:00",
  "updated_at": "2022-08-09T17:30:52.647004+00:00",
  "status": "enabled",
  "object_type": "Subscription"
}
```

## Verifying the sender

There are a few methods for securing webhook deliveries. We calculate a
signature of the request using the shared secret and send it with the HTTP
request to your webhook URL. Additionally, we try to only include references to
objects in the request bodies. These references can then be used to make calls
to the Partner API, using valid API credentials.

### Calculating the signature

HMAC-SHA256 is used to calculate the signature contained in the
`x-jiko-signature` HTTP request header sent with the webhook request.

The method is as follows:

`base64(hmac_sha256(shared_secret, request_body))`

where

- `shared_secret` is the shared secret specified during the subscription
creation.
- `request_body` is the entire utf-8 encoded string containg the webhook request
body.


In the following two examples, we have a webhook API setup for listening to
webhook calls from the Partner API, having already created a subscription with a
shared secret:


```python
import base64
import hashlib
import hmac
import json
import os

from fastapi import FastAPI, HTTPException, Request


# we've setup an environment variable containing our shared secret
SHARED_SECRET = os.getenv("SHARED_SECRET", "")


def calculate_signature(request_bytes: bytes) -> str:
    hashed = hmac.new(
        SHARED_SECRET.encode("utf-8"), request_bytes, hashlib.sha256
    ).digest()

    return base64.b64encode(hashed).decode("utf-8")


app = FastAPI()


@app.post("/webhook", status_code=202)
async def receive_webhook(request: Request) -> None:
    body_bytes = await request.body()

    # we calculate the signature using the request body
    signature = calculate_signature(body_bytes)

    if request.headers["x-jiko-signature"] != signature:
        raise HTTPException(401, "Invalid signature!")

    # Okay, this seems to have come from Jiko, so now
    # we can do something with the request data!
    request_json = json.loads(body_bytes)

    if request_json["event_type"] == "application.approved":
        application_id = request_json["payload"]["application_id"]

        # ...
```

Here's a similar example in Node.js, using
[Express.js](https://www.npmjs.com/package/express).


```js
const express = require('express')
const bodyParser = require('body-parser')
const crypto = require('crypto')

function calculateSignature(requestJson, sharedSecret) {
  const hashed = crypto
    .createHmac('sha256', sharedSecret)
    .update(requestJson)
    .digest()

  return Buffer.from(hashed).toString('base64')
}

const app = express()
  .use(bodyParser.text({ type: '*/*' }))
  .post('/webhook', function (req, res) {
    const sig = calculateSignature(req.body, process.env.SHARED_SECRET)

    if (sig !== req.headers['x-jiko-signature']) {
      res.status(401).send('Invalid signature!')
      return
    }

    // do stuff with the request body
    const { payload, event_type } = JSON.parse(req.body)

    if (event_type === 'application.approved') {
      console.log(
        "I'm about to fetch this application from the Partner API!",
        payload.application_id
      )
    }

    res.status(202).send()
  })

app.listen(3000, () => console.log('Listening on 3000!'))
```