Zapier integration with ERPNext

@MichaelPinkowski let’s see if we can get the team to take a look at the Zapier capabilities. There are so many potential uses for this, it could accelerate some of our customer adoption.

3 Likes

I’m using Zapier actions to accomplish the following:

  1. Listen via webhook for a transaction from a marketplace site
  2. Call the marketplace api to get some order details
  3. Parse and error check
  4. Construct a json object to create a new Sales Invoice in ERPNext

The new token-based authentication in ERPNext V11 made it possible. Trying to authenticate in prior versions was problematic.

1 Like

@Mike_Z any chance you could help with a step-by-step guide to setting up Zapier for ERPNext?

Sure, I’ll put something together.
Mike

2 Likes

Hi,
I’ve been away at my son’s wedding, sorry for the delay.

Creating a record via Zapier
This works with ERPNext v11, as we’re relying on the API Access feature of the User DocType to give us token-based authentication

  1. Generate API Access keys in ERPNext. Good process would be to create a Zapier user dedicated to this purpose. You will need the API Key and API Secret

  2. In Zapier, add a Webhook Action to your zap. This should be after the point in your zap that you’ve rendered all of the necessary data that you will be writing to ERPNext.

  3. Use the “Custom Request Action”.

  4. Method = “Post” if you’re creating a new record

  5. URL like https://erp.yoursite.com/api/resource/Sales Invoice for creating a Sales invoice

  6. For “Data”, enter the raw json to create the record. What I did was to use Postman to read a Sales Invoice, then pare the json down to the fields I needed. I then validated the json using one of the web tools avalable to make sure I didn’t drop any braces, etc. To learn and play, start with a simple DocType first, like a Customer or an Item

  7. You need three headers: Content-Type = application/json, Accept = application/json and Authorization. Authorization is “token {apikey}:{apisecret}”, (token space apikey colon apisecret) like
    token 819abcxxxxx558e:4da5xxxxxxxx292

Hope that helps!
Mike

5 Likes

@Mike_Z Interesting idea to use raw requests. Did you also try to use the app? If so, what were problems that you encountered?

@flexy2ky Heres a guide on how to set up the app:

@rmeyer I was already pulling marketplace transactions into a Google sheet for erpnext import with a zap, so replacing it with a custom webhook post was easy.

Here’s an actual json construct for Zapier. All elements in double braces represent Zapier data from prior steps. A lot of the data is fixed, such as currency, naming_series, etc.

{
“naming_series”: “REV-”,
“company”: “Safe Haven Music”,
“posting_date”: “{{52735607__output}}”,
“currency”: “USD”,
“conversion_rate”: 1,
“selling_price_list”: “Standard Selling”,
“price_list_currency”: “USD”,
“plc_conversion_rate”: 1,
“base_net_total”: {{54716025__total__amount}},
“base_grand_total”: {{54716025__direct_checkout_payout__amount}},
“grand_total”: {{54716025__direct_checkout_payout__amount}},
“debit_to”: “1310 - Acct Receivable - SHM”,
“customer”: “Reverb Customer”,
“is_pos”: 1,
“pos_profile”: “Reverb”,
“due_date”: “{{52735607__output}}”,
“update_stock”: 1,
“base_total”: {{54716025__total__amount}},
“total”: {{54716025__total__amount}},
“net_total”: {{54716025__total__amount}},
“status”: “Draft”,
“market_place_order_id”:“{{54716025__order_number}}”,
“items”: [
{
“base_amount”: {{54716025__amount_product__amount}},
“qty”: 1,
“rate”: {{54716025__amount_product__amount}},
“cost_center”: “Main - SHM”,
“stock_uom”: “Each”,
“item_name”: “{{63048145__output}}”,
“amount”: {{54716025__amount_product__amount}},
“conversion_factor”: 1,
“warehouse”: “Retail Store - SHM”,
“uom”: “Each”,
“description”: “{{63048145__output}}”,
“base_rate”: {{54716025__amount_product__amount}},
“item_code”: “{{54716025__sku}}”,
“income_account”: “4110 - Product Sales - SHM”,
“expense_account”: “5110 - Cost of Goods Sold - SHM”
}
],
“taxes”: [
{
“cost_center”: “Main - SHM”,
“charge_type”: “Actual”,
“description”: “Reverb Shipping Label per Order (neg)”,
“account_head”: “5166 - Shipping Labels - SHM”,
“tax_amount”: -{{62410989__output}}
},
{
“cost_center”: “Main - SHM”,
“charge_type”: “Actual”,
“description”: “Reverb Processing Fee per Order (neg)”,
“account_head”: “5162 - Processing Fees - SHM”,
“tax_amount”: -{{54716025__direct_checkout_fee__amount}}
},
{
“cost_center”: “Main - SHM”,
“charge_type”: “Actual”,
“description”: “Reverb Selling Fee per Order (neg)”,
“account_head”: “5164 - Selling Fees - SHM”,
“tax_amount”: -{{54716025__selling_fee__amount}}
},
{
“cost_center”: “Main - SHM”,
“charge_type”: “Actual”,
“description”: “Reverb Bump Fees per Order (neg)”,
“account_head”: “5164 - Selling Fees - SHM”,
“tax_amount”: -{{62410764__output}}
},
{
“cost_center”: “Main - SHM”,
“charge_type”: “Actual”,
“description”: “Amount Collected from Customer for Shipping (pos)”,
“account_head”: “4115 - Shipping Sales - SHM”,
“tax_amount”: {{54716025__shipping__amount}}
}
],
“payments”: [
{
“account”: “Reverb Bucks - SHM”,
“mode_of_payment”: “{{54716025__payment_method}}”,
“default”: 0,
“amount”: {{54716025__direct_checkout_payout__amount}},
“type”: “Bank”
}
]
}<

Thanks a lot for the installation guide. I was able to deploy the app successfully but when trying to test a zap by sending a mail on submit for Purchase Order, I get this error:

Response: { “exc”: “["Traceback (most recent call last):\n File \"/home/saandb/frappe-bench/apps/frappe/frappe/app.py\", line 60, in application\n response = frappe.api.handle()\n File \"/home/saandb/frappe-bench/apps/frappe/frappe/api.py\", line 116, in handle\n data = json.loads(frappe.local.form_dict.data)\n File \"/usr/lib/python3.6/json/init.py\", line 348, in loads\n ‘not {!r}’.format(s.class.name))\nTypeError: the JSON object must be str, bytes or bytearray, not ‘NoneType’\n"]” } What happened (You are seeing this because you are an admin): Starting POST request to http://dev.flexcomsystems.net/api/resource/Webhook Received 500 code from http://dev.flexcomsystems.net/api/resource/Webhook after 77ms Received content “{“exc”:”["Traceback (most recent call last):\n File \"/home/saandb/frappe-bench/apps/frappe/fra" Response: { “exc”: “["Traceback (most recent call last):\n File \"/home/saandb/frappe-bench/apps/frappe/frappe/app.py\", line 60, in application\n response = frappe.api.handle()\n File \"/home/saandb/frappe-bench/apps/frappe/frappe/api.py\", line 116, in handle\n data = json.loads(frappe.local.form_dict.data)\n File \"/usr/lib/python3.6/json/init.py\", line 348, in loads\n ‘not {!r}’.format(s.class.name))\nTypeError: the JSON object must be str, bytes or bytearray, not ‘NoneType’\n"]” } Console logs: POST :censored:29:de49cbe66a:/api/resource/Webhook returned HTTP 500: Headers: { “Content-Type”: “application/json”, “user-agent”: “Zapier”, “Authorization”: “:censored:6:c2dc31949c: :censored:30:0d49aa3da6:”, “Accept”: “application/json” } Params: undefined Body: “{"request_url":"https://zapier.com/hooks/standard/5479441/7e2e677530104e6bb8358481a6b1679b/\",\“webhook_doctype\”:\"Purchase Order","webhook_docevent":"on_submit","webhook_data":[{"fieldname":"name","key":"id"}]}” Stack trace: “exc”: “["Traceback (most recent call last):\n File \"/home/saandb/frappe-bench/apps/frappe/frappe/app.py\", line 60, in application\n response = frappe.api.handle()\n File \"/home/saandb/frappe-bench/apps/frappe/frappe/api.py\", line 116, in handle\n data = json.loads(frappe.local.form_dict.data)\n File \"/usr/lib/python3.6/json/init.py\", line 348, in loads\n ‘not {!r}’.format(s.class.name))\nTypeError: the JSON object must be str, bytes or bytearray, not ‘NoneType’\n"]” } mustBe200 (/var/task/middleware/response.js:57:11) Object.collector.then.newOutput (/var/task/node_modules/zapier-platform-core/src/middleware.js:80:37) bound (domain.js:301:14) Object.runBound (domain.js:314:12) Object.tryCatcher (/var/task/node_modules/bluebird/js/release/util.js:16:23) Promise._settlePromiseFromHandler (/var/task/node_modules/bluebird/js/release/promise.js:512:31) Promise._settlePromise (/var/task/node_modules/bluebird/js/release/promise.js:569:18) Promise._settlePromise0 (/var/task/node_modules/bluebird/js/release/promise.js:614:10) Promise._settlePromises (/var/task/node_modules/bluebird/js/release/promise.js:693:18) Async._drainQueue (/var/task/node_modules/bluebird/js/release/async.js:133:16) Async._drainQueues (/var/task/node_modules/bluebird/js/release/async.js:143:10) Immediate.Async.drainQueues (/var/task/node_modules/bluebird/js/release/async.js:17:14) runCallback (timers.js:794:20) tryOnImmediate (timers.js:752:5) processImmediate [as _immediateCallback] (timers.js:729:5)

Now I can’t even test connection

Check your json. In Zapier, look at Task History to see the json you’re sending to ERPNext. Copy it and paste it into a json validator, like jsonlint.com

Couple of things I encountered were:

Undefined data - look for curly brace elements in your json. there shouldn’t be any. Here’s a missing item_code
“item_code”: “{{54716025__sku}}”,

Quotes. Make sure you have double quotes around every Zapier element that’s not a numeric. Including dates

Pleas format the error message so everybody can read it :wink:

Traceback (most recent call last):

File \"/home/saandb/frappe-bench/apps/frappe/frappe/app.py\", line 60, in application
 response = frappe.api.handle()
 File \"/home/saandb/frappe-bench/apps/frappe/frappe/api.py\", line 116, in handle
 data = json.loads(frappe.local.form_dict.data)
 File \"/usr/lib/python3.6/json/init.py\", line 348, in loads
 ‘not {!r}’.format(s.class.name))
TypeError: the JSON object must be str, bytes or bytearray, not ‘NoneType’

What happened (You are seeing this because you are an admin): 

Starting POST request to http://dev.flexcomsystems.net/api/resource/Webhook 
Received 500 code from http://dev.flexcomsystems.net/api/resource/Webhook after 77ms 

Console logs: 
POST http://dev.flexcomsystems.net/api/resource/Webhook returned HTTP 500: 
Headers: { 
	"Content-Type": "application/json", 
	"user-agent": "Zapier", 
	"Authorization": ":censored:6:c2dc31949c: :censored:30:0d49aa3da6:", 
	"Accept": "application/json" 
} 

Params: undefined 
Body:

{
	"request_url": "https://zapier.com/hooks/standard/xxxxxxxx/xxxxxxxxxx/",
	"webhook_doctype": "Purchase 1 Order",
	"webhook_docevent": "on_submit",
	"webhook_data": [
		{
			"fieldname":"name",
			"key":"id"
		}
	]
}

This seems to be a general bug in frappe – it’s not possible to create new documents via the API at the moment:

https://github.com/frappe/frappe/issues/8230

Hi. I’m also facing this issue with the Zapier app. I’m running into the same issue. When creating leads using zapier I get the error below. Running V 11.1.61. Would appreciate some assistance

What happened(You are seeing this because you are an admin):
	Starting POST request to https: //erp.co.ke/api/resource/Lead
	Received 500 code from https: //erp.co.ke/api/resource/Lead after 287ms
	Received content "<html> <
	head >
	<
	title > Internal Server Error < /title> <
	/head> <
	body >
	<
	h1 > < p > Internal Serv "
Expected a JSON response.Got undefined.

Console logs:

	Stack trace:
	mustBeJson(/var/task / middleware / response.js: 25: 11)
Object.collector.then.newOutput(/var/task / node_modules / zapier - platform - core / src / middleware.js: 80: 37)
bound(domain.js: 301: 14)
Object.runBound(domain.js: 314: 12)
Object.tryCatcher(/var/task / node_modules / bluebird / js / release / util.js: 16: 23)
Promise._settlePromiseFromHandler(/var/task / node_modules / bluebird / js / release / promise.js: 512: 31)
Promise._settlePromise(/var/task / node_modules / bluebird / js / release / promise.js: 569: 18)
Promise._settlePromise0(/var/task / node_modules / bluebird / js / release / promise.js: 614: 10)
Promise._settlePromises(/var/task / node_modules / bluebird / js / release / promise.js: 693: 18)
Async._drainQueue(/var/task / node_modules / bluebird / js / release / async.js: 133: 16)
Async._drainQueues(/var/task / node_modules / bluebird / js / release / async.js: 143: 10)
Immediate.Async.drainQueues(/var/task / node_modules / bluebird / js / release / async.js: 17: 14)
runCallback(timers.js: 794: 20)
tryOnImmediate(timers.js: 752: 5)
processImmediate[as _immediateCallback](timers.js: 729: 5)