title: Webhook Payload Result is null in Dialogflow but not in Postman or Locally
body: <p>I am <strong>failing to send over my personal banking data via a flask webhook from the Nordigen API to Dialogflow via fulfilment as only null is being received within the Dialogflow payload</strong>:</p>
  <pre><code>{
    &quot;fulfillmentText&quot;: &quot;Your text response&quot;,
    &quot;fulfillmentMessages&quot;: [
      {
        &quot;payload&quot;: [
          null
        ]
      }
    ]
  }
  </code></pre>
  <p>The webhook error message is: Webhook call failed. Error: Failed to parse webhook JSON response: Expect a map object but found: [null].</p>
  <p>When I just send the data as a fulfillmentText I receive <code>&quot;fulfillmentText&quot;: null.</code></p>
  <p>I have tested my webhook with postman and there - as well as locally and other webhook'esque tests - <strong>everything is fine as I receive my most recent banking data</strong>.</p>
  <p>The overall flow is twofold and simple:</p>
  <ol>
  <li><p>User gets the correct banking and user specific login link to a specified bank, copy &amp; pastes it to perform banking login by <code>query_text = 'login'</code>.</p>
  </li>
  <li><p>After a successful banking login the user can fetch different kinds of banking data (like balance) by <code>query_text = 'balance'</code>.</p>
  </li>
  </ol>
  <p>I went crazy with overengineering the flask webhook as I tried out many different things like asynchronous functions, uploading my Flask app to Heroku or CORS. I have even implemented an OAuth2 process where the user would <code>query_text = 'google auth'</code> and initiate the OAuth2 process in a step 0) by creating OAuth2 credentials and the Flask-Dance Python package. (Even though I have hardcoded the OAuth2 redirect link but this shouldn't be an issue atm). I was even trying to trick Dialogflow by creating a small Sqlite3 db within my webhook to at least upload the data there and then use it but without success.</p>
  <p>So my question is .. what am I missing here? Why do I receive my banking data everywhere else but not in Dialogflow. My intuition is telling me Google is blocking this data for whatever reason.</p>
  <p>Honestly I just don't know how to continue and I would appreciate <strong>any</strong> helpful comments!</p>
  <p>This is my Flask webhook:</p>
  <pre><code>from dialogflow_fulfillment import QuickReplies, WebhookClient, Payload
  from flask import Flask, request, jsonify, make_response, session, render_template, redirect, url_for
  from flask_cors import CORS, cross_origin
  import json
  from json import JSONEncoder
  import os
  import asyncio
  import requests
  import sqlite3
  
  from app.src.handler_login import handler_login
  from app.src.handler_balance import handler_balance
  from app.banking_data.init_db import create_connection
  
  from flask_dance.contrib.google import make_google_blueprint, google
  
  from oauthlib.oauth2.rfc6749.errors import InvalidGrantError, TokenExpiredError, OAuth2Error
  
  from google.cloud import dialogflow_v2beta1 as dialogflow
  from google.oauth2 import service_account
  
  from uuid import uuid4
  
  from nordigen import NordigenClient
  
  
  
  # NORDIGEN
  # Credentials
  secret_id=&quot;XXX&quot;
  secret_key=&quot;XXX&quot;
  
  # Configuration
  institution_id = &quot;XXX&quot;
  
  
  app = Flask(__name__)
  # set Flask secret key
  app.secret_key = os.environ.get(&quot;FLASK_SECRET_KEY&quot;, &quot;supersekrit&quot;)
  
  
  # GOOGLE API &amp; AUTHENTICATION
  app.config[&quot;GOOGLE_OAUTH_CLIENT_ID&quot;] = &quot;XXX&quot;
  
  app.config[&quot;GOOGLE_OAUTH_CLIENT_SECRET&quot;] = &quot;XXX&quot;
  os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = &quot;1&quot;
  os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = &quot;1&quot;
  
  google_bp = make_google_blueprint(scope=[&quot;profile&quot;, &quot;email&quot;])
  app.register_blueprint(google_bp, url_prefix=&quot;/login&quot;)
  
  app.config['CORS_HEADERS'] = 'Content-Type'
  
  cors = CORS(app, supports_credentials=True, resources={r&quot;/webhook&quot;: {&quot;origins&quot;: &quot;*&quot;}})
  
  
  client = NordigenClient(
          secret_id=secret_id,
          secret_key=secret_key
          )
          
  client.generate_token()
  
  
  # subclass JSONEncoder
  class setEncoder(JSONEncoder):
          def default(self, obj):
              return list(obj)
  
     
  @app.route(&quot;/&quot;)
  def index():
  
      if not google.authorized:
          return redirect(url_for(&quot;google.login&quot;))
      try:
          resp = google.get(&quot;/oauth2/v1/userinfo&quot;)
          assert resp.ok, resp.text
          return &quot;You are {email} on Google&quot;.format(email=resp.json()[&quot;email&quot;])
      except (InvalidGrantError, TokenExpiredError) as e:  # or maybe any OAuth2Error
          return redirect(url_for(&quot;google.login&quot;))
  
  @app.route('/webhook', methods=['GET', 'POST', 'OPTION'])
  async def webhook():
          
      &quot;&quot;&quot;Handles webhook requests from Dialogflow.&quot;&quot;&quot;
  
      req = request.get_json(force=True)
          
      query_text = req.get('queryResult').get('queryText')
  
      if query_text:
  
          if query_text == 'google auth':
  
              if not google.authorized:
                  auth_link = 'MY HARD CODED GOOGLE AUTHENTICATION LINK HERE'
                  
                  auth_link =  {
                      &quot;fulfillmentText&quot;: auth_link,
                      &quot;source&quot;: 'webhook'
                      }
                  
                  return auth_link
              try:
                  resp = google.get(&quot;/oauth2/v1/userinfo&quot;)
                  assert resp.ok, resp.text
                  return &quot;You are {email} on Google&quot;.format(email=resp.json()[&quot;email&quot;])
              except (InvalidGrantError, TokenExpiredError) as e:  # or maybe any OAuth2Error
                   
                  auth_link = 'MY HARD CODED GOOGLE AUTHENTICATION LINK HERE'
                  
                  auth_link =  {
                      &quot;fulfillmentText&quot;: auth_link,
                      &quot;source&quot;: 'webhook'
                      }
                  return auth_link
  
          if query_text == 'login':
          
              link = await handler_login(client, institution_id, session)
                  
              link =  {
                      &quot;fulfillmentText&quot;: link,
                      &quot;source&quot;: 'webhook'
                      }
  
              link = make_response(jsonify(link))
  
              link.headers.add('Access-Control-Allow-Origin', '*')
  
              return link
  
  
          if query_text == 'balance':
  
              balance = await handler_balance(client, session)
              
              balance = {
                              &quot;fulfillmentText&quot;: &quot;Your text response&quot;,
                              &quot;fulfillmentMessages&quot;: [
                                   {
                              
                                       &quot;text&quot;: {
                                           &quot;text&quot;: [
                                               &quot;Your text response&quot;
                                           ]
                                       }
                                   },
                                  {
                                      &quot;payload&quot;: {
                                          balance
                                      }
                                  }
                              ]
                          }
  
              balance = json.dumps(balance, indent=4, cls=setEncoder)
                  
              balance = make_response(balance)
  
              return balance
                  
  
  if __name__ == &quot;__main__&quot;:
      app.run(debug=True)
  
  </code></pre>
  <p>Here are two helper functions I have created that perform the creation of the login link the the fetching of my banking data via Nordigen:</p>
  <pre><code>from uuid import uuid4
  
  
  async def handler_login(client, institution_id, session):
  
      &quot;&quot;&quot;Handles the webhook request.&quot;&quot;&quot;
  
      # Initialize bank session
      init = client.initialize_session(
              # institution id
              institution_id=institution_id,
              # redirect url after successful authentication
              redirect_uri=&quot;https://nordigen.com&quot;,
              # additional layer of unique ID defined by you
              reference_id=str(uuid4())
                  )
  
      link = init.link
  
      session[&quot;req_id&quot;] = init.requisition_id
  
      return link
  </code></pre>
  <pre><code>async def handler_balance(client, session):
  
      if &quot;req_id&quot; in session:
          # Get account id after you have completed authorization with a bank
          # requisition_id can be gathered from initialize_session response
  
          #requisition_id = init.requisition_id
          
          accounts = client.requisition.get_requisition_by_id(
              requisition_id=session[&quot;req_id&quot;]
          )
  
          # Get account id from the list.
          account_id = accounts[&quot;accounts&quot;][0]
          #account_id = accounts[&quot;id&quot;]
  
          # Create account instance and provide your account id from previous step
          account = client.account_api(id=account_id)
  
          # Fetch account metadata
          #meta_data = account.get_metadata()
          # Fetch details
          #details = account.get_details()
          # Fetch balances
          balance = account.get_balances()
          balance = balance[&quot;balances&quot;][0]
          balance = balance[&quot;balanceAmount&quot;][&quot;amount&quot;]
          #balance = json.loads(balance)
          # Fetch transactions
          #transactions = account.get_transactions()
  
          #agent.add(Payload({'balance': balance}))
  
          return balance 
  </code></pre>
  <p>Feel free to comment if you need any more input!</p>