Zapier integration with ERPNext

any update on this integration, wish to contribute

Really like to have Zapier integration…

Sure enough it’s here on the request wish todo list Integration to Zapier · Issue #9943 · frappe/erpnext · GitHub

But not here on the bounty list https://erpnext.org/bounties

1 Like

Hello everyone,

I recently published the first ERPNext app for Zapier.

Features:

  • Search all records in ERPNext.
  • Create a new record in ERPNext with data from any Zapier app.
  • Trigger any Zapier app when something happens in ERPNext.
  • Works for all DocTypes. (Especially well for Leads and Opportunities.)

Please email me about a custom integration (raffael@alyf.de / German Website) and feel free to contribute on Github.

Best regards,
Raffael

11 Likes

Hi

I cannot seem to see this app on Zapier.

Hi Olamide,

you are correct, it’s not a public app on zapier. You have to deploy it as a private app. This has the following reasons:

  1. ERPNext has no single API (like Facebook) but, in contrast, everybody has his own ERPNext instance with his own API and oAuth clients.

  2. I’m not the ERPNext foundation, so I should not publish an app in their name.

The consequence is, that you have to deploy the app privately to Zapier, using the Zapier command line interface tool.

Afterwards, “ERPNext v0.0.1” will appear in your list of available apps on Zapier.

If you cannot deploy the app on your own, I’ll be happy to help. (raffael@alyf.de)

Best regards,

Raffael

3 Likes

Hi-Is your app limited to only Leads and opportunities? e.g if we wanted to do the following use cases

Sales invoice creation on My ERP instance> Zapier>Messaging App e.g Twillo or ZipWhip or
Appointment created on myERPNext instance> Zapier> Integrated Zapier chatbot

What changes to this app do we need to make?

It’s not limited to a specific DocType. You can can create a trigger for any event of any DocType. For example, on_submit of Sales Invoice. No changes required.

3 Likes

@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)