{"__v":2,"_id":"5845a4a89f6fbb1b004307c1","category":{"version":"5845a4a89f6fbb1b004307b7","project":"54d3007669578e0d002730c9","_id":"5845a4a89f6fbb1b004307bb","__v":0,"sync":{"url":"","isSync":false},"reference":false,"createdAt":"2016-06-23T05:36:52.229Z","from_sync":false,"order":3,"slug":"fulfillment","title":"Fulfillment"},"parentDoc":null,"project":"54d3007669578e0d002730c9","user":"55bf6cdcad601c2b00762d13","version":{"__v":1,"_id":"5845a4a89f6fbb1b004307b7","project":"54d3007669578e0d002730c9","createdAt":"2016-12-05T17:32:24.708Z","releaseDate":"2016-12-05T17:32:24.708Z","categories":["5845a4a89f6fbb1b004307b8","5845a4a89f6fbb1b004307b9","5845a4a89f6fbb1b004307ba","5845a4a89f6fbb1b004307bb","5845a4a89f6fbb1b004307bc","5845a4a89f6fbb1b004307bd","5845a4a89f6fbb1b004307be","5845a4a89f6fbb1b004307bf","5845a4a89f6fbb1b004307c0"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"25.0.0","version":"25"},"updates":["57f65e305cf2a02200799035","58588ae0bda6ca31008a9b17","586cab804302651900a83c5d"],"next":{"pages":[],"description":""},"createdAt":"2016-02-24T23:32:38.378Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":0,"body":"* [Overview](#overview)\n* [Webhook Requirements](#webhook-requirements)\n * [Authentication](#section-authentication)\n * [Format of Request to the Service](#section-format-of-request-to-the-service)\n * [Format of Response from the Service](#section-format-of-response-from-the-service)\n * [Errors](#section-errors)\n* [Limits](#limits)\n* [Webhook for Slot Filling](#webhook-for-slot-filling)\n* [Webhook for Domains](#webhook-for-domains)\n* [Webhook Example](#webhook-example)\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Overview\"\n}\n[/block]\n**Webhook** integration allows you to pass information from a matched intent into a web service and get a result from it.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Webhook Requirements\"\n}\n[/block]\nThe web service that you want to integrate with API.AI should meet the following requirements.\n\n##Authentication\n\nAuthentication can be done in 2 ways:\n\n* basic authentication with login and password\n* authentication with additional authentication headers\n\nIf the integrated service doesn’t require any authentication, leave the authentication fields blank.\n\nThe service should preferably use HTTPS.  \n[block:callout]\n{\n  \"type\": \"info\",\n  \"body\": \"The URL used for accessing your web service should be a public URL.\"\n}\n[/block]\n##Format of Request to the Service\n\nWhen an intent in which a webhook was enabled is triggered, API.AI sends data to the service in the form of POST request with a POST body in the format of <a href=\"https://docs.api.ai/docs/query#response\" target=\"_blank\">response to a query</a>.\n\nIf a request is sent from one of the messaging platforms, the \"originalRequest\" field is added to the response to a query. \n\nThis format is chosen in order to simplify the response parsing on the service side with the help of API.AI SDKs.\n\n###Sample Request to the Service\n```   \n POST https://my-service.com/action\n    \n Headers:\n //user defined headers\n Content-type: application/json\n    \n POST body:\n \n {\n     \"originalRequest\": {\n         \"data\": {\n             \"text\": \"shipping costs for asia\", \n             \"match\": [\n                 \"shipping costs for asia\"\n             ], \n             \"type\": \"message\", \n             \"event\": \"direct_message\", \n             \"team\": \"T0FJ03RMP\", \n             \"user\": \"U0FLW1N95\", \n             \"channel\": \"D4VTEALFP\", \n             \"ts\": \"1478131884.000006\"\n         }, \n         \"source\": \"slack_testbot\"\n     }, \n     \"timestamp\": \"2016-11-03T00:11:24.706Z\", \n     \"result\": {\n         \"speech\": \"\", \n         \"fulfillment\": {\n             \"speech\": \"Shipping cost varies for the location the item needs to be shipped to. We will show you the cost, when you form the order in your cart.\", \n             \"messages\": [\n                 {\n                     \"speech\": \"Shipping cost varies for the location the item needs to be shipped to. We will show you the cost, when you form the order in your cart.\", \n                     \"type\": 0\n                 }\n             ]\n         }, \n         \"score\": 1.0, \n         \"source\": \"agent\", \n         \"action\": \"shipping.cost\", \n         \"resolvedQuery\": \"shipping costs for asia\", \n         \"actionIncomplete\": false, \n         \"contexts\": [\n             {\n                 \"name\": \"generic\", \n                 \"parameters\": {\n                     \"slack_channel\": \"D2VTEALFP\", \n                     \"shipping-zone.original\": \"Asia\", \n                     \"shipping-zone\": \"Asia\", \n                     \"slack_user_id\": \"U0FLW1N93\"\n                 }, \n                 \"lifespan\": 4\n             }\n         ], \n         \"parameters\": {\n             \"shipping-zone\": \"Asia\"\n         }, \n         \"metadata\": {\n             \"intentId\": \"e2eb1b9c-761d-4588-8b00-d7062128cb51\", \n             \"webhookUsed\": \"true\", \n             \"intentName\": \"shipping.cost\"\n         }\n     }, \n     \"sessionId\": \"fa08b2f0-a0f3-11e6-9a45-ef317d100c6e\", \n     \"id\": \"cc8ca971-0eec-4a04-ab54-d2af01e4674e\", \n     \"status\": {\n         \"errorType\": \"success\", \n         \"code\": 200\n     }\n }\n```\n\n##Format of Response from the Service\n\nThe response from the service should have the following fields:\n[block:parameters]\n{\n  \"data\": {\n    \"h-0\": \"Name\",\n    \"h-1\": \"Type\",\n    \"h-2\": \"Description\",\n    \"0-0\": \"speech\",\n    \"1-0\": \"displayText\",\n    \"2-0\": \"data\",\n    \"3-0\": \"contextOut\",\n    \"4-0\": \"source\",\n    \"0-1\": \"String\",\n    \"1-1\": \"String\",\n    \"4-1\": \"String\",\n    \"2-1\": \"Dictionary of objects\",\n    \"3-1\": \"Array of context objects\",\n    \"0-2\": \"Voice response to the request.\",\n    \"1-2\": \"Text displayed on the user device screen.\",\n    \"2-2\": \"Additional data required for performing the action on the client side. The data is sent to the client in the original form and is not processed by API.AI.\",\n    \"3-2\": \"Array of context objects set after intent completion.\\n\\nExample: \\n`\\\"contextOut\\\": [{\\\"name\\\":\\\"weather\\\", \\\"lifespan\\\":2, \\\"parameters\\\":{\\\"city\\\":\\\"Rome\\\"}}]`\",\n    \"4-2\": \"Data source.\"\n  },\n  \"cols\": 3,\n  \"rows\": 5\n}\n[/block]\n###Sample Response from the Service\n\n    Headers:\n    Content-type: application/json\n    \n    Body:\n    {\n    \"speech\": \"Barack Hussein Obama II is the 44th and current President of the United States.\",\n    \"displayText\": \"Barack Hussein Obama II is the 44th and current President of the United States, and the first African American to hold the office. Born in Honolulu, Hawaii, Obama is a graduate of Columbia University   and Harvard Law School, where \",\n    \"data\": {...},\n    \"contextOut\": [...],\n    \"source\": \"DuckDuckGo\"\n    }\n[block:callout]\n{\n  \"type\": \"warning\",\n  \"body\": \"The header must be “Content-type: application/json”.\"\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"info\",\n  \"body\": \"To send formatted messages to bots, use the following format for the `\\\"data\\\"` field:\\n`\\\"data\\\": {\\\"slack\\\": {<slack_message>}}`\\n`\\\"data\\\": {\\\"facebook\\\": {<facebook_message>}}`\\n`\\\"data\\\": {\\\"kik\\\": {<kik_message>}}`\\n`\\\"data\\\": {\\\"telegram\\\": {<telegram_message>}}`\"\n}\n[/block]\n**Related topics:**\n\n- <a href=\"https://docs.api.ai/docs/slack-webhook-integration-guideline\" target=\"_blank\">Slack + Webhook Integration Example</a>\n- <a href=\"https://docs.api.ai/docs/kik-webhook-integration-example\" target=\"_blank\">KIk + Webhook Integration Example</a>\n- <a href=\"https://docs.api.ai/docs/webhook-for-online-store-support-agent\" target=\"_blank\">Webhook Example for Online Store Support Agent</a>\n\n##Errors\n\nThe service should be able to handle the following errors:\n\n* 400 Bad Request – The request was invalid or cannot be served\n* 401 Unauthorized – The request requires user authentication\n* 403 Forbidden – The server understood the request but refuses to take any further action or the access is not allowed\n* 404 Not found – There is no resource behind the URI\n* 500 Server fault – Internal Server Error\n* 503 Service Unavailable – Internal Server Error\n\nIn case of these or other errors (timeout limit exceeded, service is not available), the “status” in the response sent to the client will contain the following:\n\n    \"status\": {\n            \"code\": 206,\n            \"errorType\": \"webhook call failed with %error Code% error\"\n          }\n\n where <code>error Code</code> is the error ID received from the service.\n[block:callout]\n{\n  \"type\": \"info\",\n  \"body\": \"In the case of an error or empty response, API.AI will send a <a href=\\\"https://docs.api.ai/docs/query#response\\\" target=\\\"_blank\\\">standard response to the /query</a> to the client. In this case, the agent response will contain the ‘Response’ content defined in the triggered intent.\"\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Limits\"\n}\n[/block]\n* Timeout for service response – 5 seconds.\n* Data received in the response from the service – up to 64K.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Webhook for Slot Filling\"\n}\n[/block]\nIf you want to send requests for required parameters from API.AI to your web service, check the option 'Use webhook for slot filling' in the 'Fulfillment' section of the intent. \n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/5ce109e-Webhook-for-Slot-filling.png\",\n        \"Webhook-for-Slot-filling.png\",\n        2032,\n        1342,\n        \"#f8f8f8\"\n      ],\n      \"sizing\": \"full\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Webhook for Domains\"\n}\n[/block]\nIf you want to send requests matched by <a href=\"https://docs.api.ai/docs/domains\" target=\"_blank\">Domains</a> to your web service, choose the 'Enable webhook for all domains' option in the webhook settings.\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/7040fe9-Enable-webhook-for-domains.png\",\n        \"Enable-webhook-for-domains.png\",\n        2116,\n        1276,\n        \"#f7f7f7\"\n      ],\n      \"sizing\": \"full\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Webhook Example\"\n}\n[/block]\nThe following example shows the integration of the Yahoo Weather web service into an API.AI agent through the API.AI webhook. \n\n##Step 1\nSign up to <a href=\"https://www.heroku.com/\" target=\"_blank\">Heroku</a> (or sign in if you already have an account).\n\n##Step 2\nGo to <a href=\"https://github.com/api-ai/apiai-weather-webhook-sample\" target=\"_blank\">https://github.com/api-ai/apiai-weather-webhook-sample</a> and click 'Deploy to Heroku' button.\n\n##Step 3\nHeroku website will open, and you’ll be asked to fill in the app name. Insert the app name (in our example, it’s “yahoo-weather”, but you can choose your own name) and click 'Deploy for Free' button.\n\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/k7DDLuUBTqCLBgIcPxQk_deploy-to-heroku.png\",\n        \"deploy-to-heroku.png\",\n        \"2458\",\n        \"1398\",\n        \"#5f458f\",\n        \"\"\n      ],\n      \"sizing\": \"full\"\n    }\n  ]\n}\n[/block]\nWait until 'Your app was successfully deployed.' appears.\n\n##Step 4 \n<a href=\"https://console.api.ai/api-client/#/newAgent\" target=\"_blank\">Create an API.AI agent</a> and enable webhook: \n- Click on Fulfillment in the left side menu\n- Turn Webhook on\n- Insert the link `https://[App Name].herokuapp.com/webhook` into the ‘URL’ field\n- Click ‘Save’.\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/hte9N8lqSlin63MyB9Xi_webhook-fulfillment-setting.png\",\n        \"webhook-fulfillment-setting.png\",\n        \"2048\",\n        \"1266\",\n        \"#2d9adf\",\n        \"\"\n      ],\n      \"sizing\": \"full\"\n    }\n  ]\n}\n[/block]\nFor your own service, you may want to secure it with basic auth and/or headers (if you don’t want other people using your web hooks).\n\n##Step 5\nCreate a simple <a href=\"https://docs.api.ai/docs/concept-intents\" target=\"_blank\">intent</a> that will match weather requests. The intent should have `geo-city` parameter. The action should be defined as `yahooWeatherForecast`. \n\nMake sure the ‘Use webhook’ option in the ‘Fulfillment’ section is checked. \n\nIn the ‘Text response’ field, define the response that will appear in case of web service [errors](webhook#section-errors).\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/35a33fb-Webhook-demo_weather-intent.png\",\n        \"Webhook-demo_weather-intent.png\",\n        1694,\n        1438,\n        \"#f8f8f8\"\n      ],\n      \"sizing\": \"full\"\n    }\n  ]\n}\n[/block]\n##Step 6 \nNow you can test weather requests in the API.AI test console:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/bUrIEGHdRqOJFug3c2ID_webhook-test-console-response.png\",\n        \"webhook-test-console-response.png\",\n        \"688\",\n        \"1028\",\n        \"#13a160\",\n        \"\"\n      ]\n    }\n  ]\n}\n[/block]\nIn the returned JSON, the “fulfillment” object will look like this:\n\n    \"fulfillment\": {\n      \"speech\": \"Today in Boston: Fair, the temperature is 37 F\",\n      \"source\": \"apiai-weather-webhook-sample\",\n      \"displayText\": \"Today in Boston: Fair, the temperature is 37 F\"\n    }\n    \nIn case of errors or if the web service is unavailable, the response will contain the ‘Speech Response’ text from the intent:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/H2LbJUyQSwC8Y2cVCByp_webhook-error-response.png\",\n        \"webhook-error-response.png\",\n        \"680\",\n        \"1086\",\n        \"#13a25e\",\n        \"\"\n      ]\n    }\n  ]\n}\n[/block]\nJSON response will contain the details about the webhook error:\n\n    \"status\": {\n    \t\"code\": 206,\n        \"errorType\": \"partial_content\",\n        \"errorDetails\": \"Webhook call failed. Status code 503. Error:503 Service Unavailable\"\n    }\n    \nYou can also watch this video:\n[block:embed]\n{\n  \"html\": \"<iframe class=\\\"embedly-embed\\\" src=\\\"//cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FFrxdKXuHox0%3Ffeature%3Doembed&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DFrxdKXuHox0%26feature%3Dyoutu.be&image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FFrxdKXuHox0%2Fhqdefault.jpg&key=02466f963b9b4bb8845a05b53d3235d7&type=text%2Fhtml&schema=youtube\\\" width=\\\"640\\\" height=\\\"480\\\" scrolling=\\\"no\\\" frameborder=\\\"0\\\" allowfullscreen></iframe>\",\n  \"url\": \"https://www.youtube.com/watch?v=FrxdKXuHox0&feature=youtu.be\",\n  \"title\": \"Api.ai Webhook Demo\",\n  \"favicon\": \"https://s.ytimg.com/yts/img/favicon-vflz7uhzw.ico\",\n  \"image\": \"https://i.ytimg.com/vi/FrxdKXuHox0/hqdefault.jpg\"\n}\n[/block]","excerpt":"","slug":"webhook","type":"basic","title":"Webhook"}
* [Overview](#overview) * [Webhook Requirements](#webhook-requirements) * [Authentication](#section-authentication) * [Format of Request to the Service](#section-format-of-request-to-the-service) * [Format of Response from the Service](#section-format-of-response-from-the-service) * [Errors](#section-errors) * [Limits](#limits) * [Webhook for Slot Filling](#webhook-for-slot-filling) * [Webhook for Domains](#webhook-for-domains) * [Webhook Example](#webhook-example) [block:api-header] { "type": "basic", "title": "Overview" } [/block] **Webhook** integration allows you to pass information from a matched intent into a web service and get a result from it. [block:api-header] { "type": "basic", "title": "Webhook Requirements" } [/block] The web service that you want to integrate with API.AI should meet the following requirements. ##Authentication Authentication can be done in 2 ways: * basic authentication with login and password * authentication with additional authentication headers If the integrated service doesn’t require any authentication, leave the authentication fields blank. The service should preferably use HTTPS. [block:callout] { "type": "info", "body": "The URL used for accessing your web service should be a public URL." } [/block] ##Format of Request to the Service When an intent in which a webhook was enabled is triggered, API.AI sends data to the service in the form of POST request with a POST body in the format of <a href="https://docs.api.ai/docs/query#response" target="_blank">response to a query</a>. If a request is sent from one of the messaging platforms, the "originalRequest" field is added to the response to a query. This format is chosen in order to simplify the response parsing on the service side with the help of API.AI SDKs. ###Sample Request to the Service ``` POST https://my-service.com/action Headers: //user defined headers Content-type: application/json POST body: { "originalRequest": { "data": { "text": "shipping costs for asia", "match": [ "shipping costs for asia" ], "type": "message", "event": "direct_message", "team": "T0FJ03RMP", "user": "U0FLW1N95", "channel": "D4VTEALFP", "ts": "1478131884.000006" }, "source": "slack_testbot" }, "timestamp": "2016-11-03T00:11:24.706Z", "result": { "speech": "", "fulfillment": { "speech": "Shipping cost varies for the location the item needs to be shipped to. We will show you the cost, when you form the order in your cart.", "messages": [ { "speech": "Shipping cost varies for the location the item needs to be shipped to. We will show you the cost, when you form the order in your cart.", "type": 0 } ] }, "score": 1.0, "source": "agent", "action": "shipping.cost", "resolvedQuery": "shipping costs for asia", "actionIncomplete": false, "contexts": [ { "name": "generic", "parameters": { "slack_channel": "D2VTEALFP", "shipping-zone.original": "Asia", "shipping-zone": "Asia", "slack_user_id": "U0FLW1N93" }, "lifespan": 4 } ], "parameters": { "shipping-zone": "Asia" }, "metadata": { "intentId": "e2eb1b9c-761d-4588-8b00-d7062128cb51", "webhookUsed": "true", "intentName": "shipping.cost" } }, "sessionId": "fa08b2f0-a0f3-11e6-9a45-ef317d100c6e", "id": "cc8ca971-0eec-4a04-ab54-d2af01e4674e", "status": { "errorType": "success", "code": 200 } } ``` ##Format of Response from the Service The response from the service should have the following fields: [block:parameters] { "data": { "h-0": "Name", "h-1": "Type", "h-2": "Description", "0-0": "speech", "1-0": "displayText", "2-0": "data", "3-0": "contextOut", "4-0": "source", "0-1": "String", "1-1": "String", "4-1": "String", "2-1": "Dictionary of objects", "3-1": "Array of context objects", "0-2": "Voice response to the request.", "1-2": "Text displayed on the user device screen.", "2-2": "Additional data required for performing the action on the client side. The data is sent to the client in the original form and is not processed by API.AI.", "3-2": "Array of context objects set after intent completion.\n\nExample: \n`\"contextOut\": [{\"name\":\"weather\", \"lifespan\":2, \"parameters\":{\"city\":\"Rome\"}}]`", "4-2": "Data source." }, "cols": 3, "rows": 5 } [/block] ###Sample Response from the Service Headers: Content-type: application/json Body: { "speech": "Barack Hussein Obama II is the 44th and current President of the United States.", "displayText": "Barack Hussein Obama II is the 44th and current President of the United States, and the first African American to hold the office. Born in Honolulu, Hawaii, Obama is a graduate of Columbia University and Harvard Law School, where ", "data": {...}, "contextOut": [...], "source": "DuckDuckGo" } [block:callout] { "type": "warning", "body": "The header must be “Content-type: application/json”." } [/block] [block:callout] { "type": "info", "body": "To send formatted messages to bots, use the following format for the `\"data\"` field:\n`\"data\": {\"slack\": {<slack_message>}}`\n`\"data\": {\"facebook\": {<facebook_message>}}`\n`\"data\": {\"kik\": {<kik_message>}}`\n`\"data\": {\"telegram\": {<telegram_message>}}`" } [/block] **Related topics:** - <a href="https://docs.api.ai/docs/slack-webhook-integration-guideline" target="_blank">Slack + Webhook Integration Example</a> - <a href="https://docs.api.ai/docs/kik-webhook-integration-example" target="_blank">KIk + Webhook Integration Example</a> - <a href="https://docs.api.ai/docs/webhook-for-online-store-support-agent" target="_blank">Webhook Example for Online Store Support Agent</a> ##Errors The service should be able to handle the following errors: * 400 Bad Request – The request was invalid or cannot be served * 401 Unauthorized – The request requires user authentication * 403 Forbidden – The server understood the request but refuses to take any further action or the access is not allowed * 404 Not found – There is no resource behind the URI * 500 Server fault – Internal Server Error * 503 Service Unavailable – Internal Server Error In case of these or other errors (timeout limit exceeded, service is not available), the “status” in the response sent to the client will contain the following: "status": { "code": 206, "errorType": "webhook call failed with %error Code% error" } where <code>error Code</code> is the error ID received from the service. [block:callout] { "type": "info", "body": "In the case of an error or empty response, API.AI will send a <a href=\"https://docs.api.ai/docs/query#response\" target=\"_blank\">standard response to the /query</a> to the client. In this case, the agent response will contain the ‘Response’ content defined in the triggered intent." } [/block] [block:api-header] { "type": "basic", "title": "Limits" } [/block] * Timeout for service response – 5 seconds. * Data received in the response from the service – up to 64K. [block:api-header] { "type": "basic", "title": "Webhook for Slot Filling" } [/block] If you want to send requests for required parameters from API.AI to your web service, check the option 'Use webhook for slot filling' in the 'Fulfillment' section of the intent. [block:image] { "images": [ { "image": [ "https://files.readme.io/5ce109e-Webhook-for-Slot-filling.png", "Webhook-for-Slot-filling.png", 2032, 1342, "#f8f8f8" ], "sizing": "full" } ] } [/block] [block:api-header] { "type": "basic", "title": "Webhook for Domains" } [/block] If you want to send requests matched by <a href="https://docs.api.ai/docs/domains" target="_blank">Domains</a> to your web service, choose the 'Enable webhook for all domains' option in the webhook settings. [block:image] { "images": [ { "image": [ "https://files.readme.io/7040fe9-Enable-webhook-for-domains.png", "Enable-webhook-for-domains.png", 2116, 1276, "#f7f7f7" ], "sizing": "full" } ] } [/block] [block:api-header] { "type": "basic", "title": "Webhook Example" } [/block] The following example shows the integration of the Yahoo Weather web service into an API.AI agent through the API.AI webhook. ##Step 1 Sign up to <a href="https://www.heroku.com/" target="_blank">Heroku</a> (or sign in if you already have an account). ##Step 2 Go to <a href="https://github.com/api-ai/apiai-weather-webhook-sample" target="_blank">https://github.com/api-ai/apiai-weather-webhook-sample</a> and click 'Deploy to Heroku' button. ##Step 3 Heroku website will open, and you’ll be asked to fill in the app name. Insert the app name (in our example, it’s “yahoo-weather”, but you can choose your own name) and click 'Deploy for Free' button. [block:image] { "images": [ { "image": [ "https://files.readme.io/k7DDLuUBTqCLBgIcPxQk_deploy-to-heroku.png", "deploy-to-heroku.png", "2458", "1398", "#5f458f", "" ], "sizing": "full" } ] } [/block] Wait until 'Your app was successfully deployed.' appears. ##Step 4 <a href="https://console.api.ai/api-client/#/newAgent" target="_blank">Create an API.AI agent</a> and enable webhook: - Click on Fulfillment in the left side menu - Turn Webhook on - Insert the link `https://[App Name].herokuapp.com/webhook` into the ‘URL’ field - Click ‘Save’. [block:image] { "images": [ { "image": [ "https://files.readme.io/hte9N8lqSlin63MyB9Xi_webhook-fulfillment-setting.png", "webhook-fulfillment-setting.png", "2048", "1266", "#2d9adf", "" ], "sizing": "full" } ] } [/block] For your own service, you may want to secure it with basic auth and/or headers (if you don’t want other people using your web hooks). ##Step 5 Create a simple <a href="https://docs.api.ai/docs/concept-intents" target="_blank">intent</a> that will match weather requests. The intent should have `geo-city` parameter. The action should be defined as `yahooWeatherForecast`. Make sure the ‘Use webhook’ option in the ‘Fulfillment’ section is checked. In the ‘Text response’ field, define the response that will appear in case of web service [errors](webhook#section-errors). [block:image] { "images": [ { "image": [ "https://files.readme.io/35a33fb-Webhook-demo_weather-intent.png", "Webhook-demo_weather-intent.png", 1694, 1438, "#f8f8f8" ], "sizing": "full" } ] } [/block] ##Step 6 Now you can test weather requests in the API.AI test console: [block:image] { "images": [ { "image": [ "https://files.readme.io/bUrIEGHdRqOJFug3c2ID_webhook-test-console-response.png", "webhook-test-console-response.png", "688", "1028", "#13a160", "" ] } ] } [/block] In the returned JSON, the “fulfillment” object will look like this: "fulfillment": { "speech": "Today in Boston: Fair, the temperature is 37 F", "source": "apiai-weather-webhook-sample", "displayText": "Today in Boston: Fair, the temperature is 37 F" } In case of errors or if the web service is unavailable, the response will contain the ‘Speech Response’ text from the intent: [block:image] { "images": [ { "image": [ "https://files.readme.io/H2LbJUyQSwC8Y2cVCByp_webhook-error-response.png", "webhook-error-response.png", "680", "1086", "#13a25e", "" ] } ] } [/block] JSON response will contain the details about the webhook error: "status": { "code": 206, "errorType": "partial_content", "errorDetails": "Webhook call failed. Status code 503. Error:503 Service Unavailable" } You can also watch this video: [block:embed] { "html": "<iframe class=\"embedly-embed\" src=\"//cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FFrxdKXuHox0%3Ffeature%3Doembed&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DFrxdKXuHox0%26feature%3Dyoutu.be&image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FFrxdKXuHox0%2Fhqdefault.jpg&key=02466f963b9b4bb8845a05b53d3235d7&type=text%2Fhtml&schema=youtube\" width=\"640\" height=\"480\" scrolling=\"no\" frameborder=\"0\" allowfullscreen></iframe>", "url": "https://www.youtube.com/watch?v=FrxdKXuHox0&feature=youtu.be", "title": "Api.ai Webhook Demo", "favicon": "https://s.ytimg.com/yts/img/favicon-vflz7uhzw.ico", "image": "https://i.ytimg.com/vi/FrxdKXuHox0/hqdefault.jpg" } [/block]