This is a library for the Freshsales Suite CRM for Python 3.6+.
It includes the following features from the Freshsales Suite CRM API:
- Contacts
- Marketing Lists
- Accounts
- Deals
- Notes
- Tasks
- Appointments
- Sales Activities
- Products
- Documents
- Selectors
The easiest way to install is from PyPi inside a virtualenv:
-
Create the virtualenv (Python 3.6+ supported) and activate it:
virtualenv env source env/bin/activate
-
Install from PyPi:
pip install python-freshworks-crm
-
Optionally, run the test suite:
pip install python-freshworks-crm[test] pytest
Please note the domain and API key are not real and the example will not work without changing these.
>>> from freshsales.api import API
>>> a = API('company.myfreshworks.com/crm/sales', 'fsdajfl323kj423rj2')
To find your API key, follow Freshworks CRM step-by-step solution article How to find your API key.
The API
class provides access to all the methods exposed by the Freshsales Suite CRM API.
Attributes are automatically converted to native Python objects where appropriate:
>>> a.contacts.list_contacts()[0].created_at
datetime.datetime(2023, 12, 5, 14, 7, 44)
The Contacts API is accessed by using the methods assigned to the a.contacts
instance. Contacts are loaded as instances of the freshsales.models.Contact
class.
>>> a.contacts.create_contact(first_name='Jane',
last_name='Sampleton',
emails='[email protected]',
custom_field=custom_field)
<Contact 'Jane Sampleton'>
With custom fields:
>>> custom_field = {'custom_field_1': 'custom_value_1',
'custom_field_2': 'custom_value_2'}
>>> a.contacts.create_contact(first_name='Jane',
last_name='Sampleton',
emails='[email protected]',
custom_field=custom_field)
<Contact 'Jane Sampleton'>
To access any attribute of the contact, use the dot notation:
>>> contact.first_name
'Jane'
>>> a.contacts.view_contact(1)
<Contact 'John Doe'>
>>> a.contacts.list_views()
[<View 'All Contacts'>, <View 'My Contacts'>]
>>> a.contacts.list_contacts()
[<Contact 'John Doe'>, <Contact 'Jane Sampleton'>]
Get specific page of contacts:
>>> a.contacts.list_contacts(page=2, per_page=5)
[<Contact 'John Doe'>, <Contact 'Jane Sampleton'>,...]
>>> a.contacts.update_contact(1,
first_name='Jane',
last_name='Updated')
<Contact 'Jane Updated'>
>>> a.contacts.upsert_contact(unique_identifier=('emails', '[email protected]'),
first_name='Jane',
last_name='Upserted')
<Contact 'Jane Upserted'>
>>> a.contacts.delete_contact(1)
True
>>> a.contacts.forget_contact(1)
True
>>> a.contacts.list_fields()
[<Field 'first_name'>, <Field 'last_name'>, <Field 'emails'>, ...]
>>> a.contacts.list_activities(1)
[<Activity 'Call'>, <Activity 'Email'>, <Activity 'Meeting'>, ...]
The Marketing Lists API is accessed by using the methods assigned to the a.lists
instance. Marketing Lists are loaded as instances of the freshsales.models.List
class.
>>> a.lists.create_list(name='My List')
<List 'My List'>
>>> a.lists.view_list(1)
<List 'My List'>
>>> a.lists.fetch_all_lists()
[<List 'My List'>, <List 'My Other List'>]
>>> a.lists.update_list(1,
name='My Updated List')
<List 'My Updated List'>
>>> a.lists.fetch_contacts_from_list(1)
[<Contact 'John Doe'>, <Contact 'Jane Sampleton'>]
>>> a.lists.add_contacts_to_list(1, [1, 2])
'2 contacts updated.'
>>> a.lists.remove_contacts_from_list(1, [1, 2])
'2 contacts updated.'
>>> a.lists.move_contacts_to_list(2, [1, 2])
'2 contacts updated.'
The Accounts API is accessed by using the methods assigned to the a.accounts
instance. Accounts are loaded as instances of the freshsales.models.Account
class.
>>> a.accounts.create_account(name='My Account')
<Account 'My Account'>
>>> a.accounts.view_account(1)
<Account 'My Account'>
>>> a.accounts.list_views()
[<View 'My View'>, <View 'My Other View'>]
>>> a.accounts.list_accounts()
[<Account 'My Account'>, <Account 'My Other Account'>]
Note: supports pagination similar to contacts.
>>> a.accounts.update_account(1,
name='My Updated Account')
<Account 'My Updated Account'>
>>> a.accounts.upsert_account(unique_identifier=('name', 'My Account'),
name='My Upserted Account')
<Account 'My Upserted Account'>
>>> a.accounts.clone_account(1)
<Account 'My Cloned Account'>
>>> a.accounts.delete_account(1)
True
>>> a.accounts.forget_account(1)
True
>>> a.accounts.list_fields()
[<Field 'name'>, <Field 'website'>, <Field 'industry'>, ...]
The Deals API is accessed by using the methods assigned to the a.deals
instance. Deals are loaded as instances of the freshsales.models.Deal
class.
>>> a.deals.create_deal(name='My Deal')
<Deal 'My Deal'>
>>> a.deals.view_deal(1)
<Deal 'My Deal'>
>>> a.deals.list_views()
[<View 'My View'>, <View 'My Other View'>]
>>> a.deals.list_deals()
[<Deal 'My Deal'>, <Deal 'My Other Deal'>]
Note: supports pagination similar to contacts.
>>> a.deals.update_deal(1,
name='My Updated Deal')
<Deal 'My Updated Deal'>
>>> a.deals.upsert_deal(unique_identifier=('name', 'My Deal'),
name='My Upserted Deal')
<Deal 'My Upserted Deal'>
>>> a.deals.clone_deal(1)
<Deal 'My Cloned Deal'>
>>> a.deals.delete_deal(1)
True
>>> a.deals.forget_deal(1)
True
>>> a.deals.list_fields()
[<Field 'name'>, <Field 'amount'>, <Field 'stage'>, ...]
The Notes API is accessed by using the methods assigned to the a.notes
instance. Notes are loaded as instances of the freshsales.models.Note
class.
>>> a.notes.create_note(description='My Note',
targetable_type='Contact',
targetable_id=1)
<Note 'My Note'>
>>> a.notes.update_note(1,
description='My Updated Note')
<Note 'My Updated Note'>
>>> a.notes.delete_note(1)
True
The Tasks API is accessed by using the methods assigned to the a.tasks
instance. Tasks are loaded as instances of the freshsales.models.Task
class.
>>> a.tasks.create_task(title='Sample Task',
description='This is just a sample task.',
due_date='Tue Jun 21 2099 11:00:00 GMT+0000',
owner_id=1,
targetable_id=1,
targetable_type='Contact')
<Task 'Sample Task'>`
>>> a.tasks.view_task(1)
<Task 'Sample Task'>
>>> a.tasks.list_tasks()
[<Task 'Sample Task'>, <Task 'My Other Task'>]
Note: supports pagination similar to contacts.
>>> a.tasks.update_task(1,
title='Updated Task')
<sk 'Updated Task'>
>>> a.tasks.mark_task_as_done(1)
<Task 'Updated Task'>
>>> a.tasks.delete_task(1)
True
The Appointments API is accessed by using the methods assigned to the a.appointments
instance. Appointments are loaded as instances of the freshsales.models.Appointment
class.
>>> a.appointments.appointments.create_appointment(
title='Sample Appointment',
description='This is just a sample Appointment.',
from_date='Mon Jun 20 2016 10:30:00 GMT+0530 (IST)',
end_date='Mon Jun 20 2016 11:30:00 GMT+0530 (IST)',
creater_id=1,
time_zone='Chennai',
location='Chennai, TN, India',
targetable_id=1,
targetable_type='Contact')
<Appointment 'Sample Appointment'>
>>> a.appointments.view_appointment(1)
<Appointment 'Sample Appointment'>
>>> a.appointments.list_appointments()
[<Appointment 'Sample Appointment'>, <Appointment 'My Other Appointment'>]
Note: supports pagination similar to contacts.
>>> a.appointments.update_appointment(1,
title='Updated Appointment')
< 'Updated Appointment'>
>>> a.appointments.delete_appointment(1)
True
The Sales Activities API is accessed by using the methods assigned to the a.sales_activities
instance. Sales Activities are loaded as instances of the freshsales.models.SalesActivity
class.
>>> a.sales_activities.sales_activities.create_activity(
title='My Activity',
notes='sample',
targetable_id=1,
targetable_type='Contact',
start_date='2017-12-04T17:00:00+05:30',
end_date='2017-12-04T17:30:00+05:30',
owner_id=1,
sales_activity_type_id=1)
<SalesActivity 'My Activity'>
>>> a.sales_activities.view_activity(1)
<SalesActivity 'My Activity'>
>>> a.sales_activities.list_activities()
[<SalesActivity 'My Activity'>, <SalesActivity 'My Other Activity'>]
Note: supports pagination similar to contacts.
>>> a.sales_activities.list_fields()
[<Field 'title'>, <Field 'notes'>, <Field 'start_date'>, ...]
>>> a.sales_activities.update_activity(1,
title='Updated Activity')
<Activity 'Updated Activity'>
>>> a.sales_activities.delete_activity(1)
True
The Products API is accessed by using the methods assigned to the a.products
instance. Products are loaded as instances of the freshsales.models.Product
class.
>>> a.products.create_product(
name='My Product',
description='This is a sample product',
category='Software',
is_active=True,
product_code='sample_product',
sku_number='sample_sku',
)
<Product 'My Product'>
>>> a.products.view_product(1)
<Product 'My Product'>
>>> a.products.update_product(1,
name='Updated Product')
<Product 'Updated Product'>
>>> a.products.delete_product(1)
True
>>> a.products.restore_product(1)
<Product 'Updated Product'>
>>> product_pricings = [
{"currency_code": "USD", "unit_price": 2000}
]
product = api.products.edit_product_prices(
1,
product_pricings=product_pricings
)
<Product 'Updated Product'>
>>> product = api.products.delete_product_prices(1,
[100, 101])
<Product 'Updated Product'>
>>> products = [{"id": 100}]
deal = api.products.add_products_to_deal(
1,
products=products
)
<Deal 'Updated Deal'>
>>> products = [{"id": 100, "quantity": 2}]
deal = api.products.edit_deal_products(
1,
products=products
)
<Deal 'Updated Deal'>
>>> deal = api.products.delete_all_deal_products(1)
<Deal 'Updated Deal'>
The Documents API is accessed by using the methods assigned to the a.documents
instance. Documents are loaded as instances of the freshsales.models.Document
class.
>>> a.documents.create_document(
deal_id=1,
contact_id=1,
display_name='Sample Document',
document_type='Quote',
cpq_document_template_name='Sample Template',
currency_code='USD'
)
<Document 'Sample Document'>
>>> a.documents.view_document(1)
<Document 'Sample Document'>
>>> a.documents.update_document(1,
display_name='Updated Document')
<Document 'Updated Document'>
>>> a.documents.delete_document(1)
True
>>> a.documents.restore_document(1)
<Document 'Updated Document'>
>>> a.documents.forget_document(1)
True
>>> products = [
{"id": 100, "quantity": 2}
]
document = api.documents.add_products_to_document(
1,
products=products
)
<Document 'Updated Document'>
>>> products = [
{"id": 100, "quantity": 3}
]
document = api.documents.edit_products_of_document(
1,
products=products
)
<Document 'Updated Document'>
>>> document = api.documents.delete_products_from_document(1)
<Document 'Updated Document'>
The Selectors API is accessed by using the methods assigned to the a.selectors
instance. Selectors are loaded as instances of the freshsales.models.Selector
class.
>>> a.selectors.get_users()
[<User 'John Doe'>, <User 'Jane Doe'>]
>>> a.selectors.get_territories()
[<Territory 'North America'>, <Territory 'South America'>]
>>> a.selectors.get_deal_stages()
[<DealStage 'Prospecting'>, <DealStage 'Qualification'>]
>>> a.selectors.get_currencies()
[<Currency 'USD'>, <Currency 'EUR'>]
>>> a.selectors.get_deal_reasons()
[<DealReason 'New Business'>, <DealReason 'Renewal'>]
>>> a.selectors.get_deal_types()
[<DealType 'New Business'>, <DealType 'Renewal'>]
>>> a.selectors.get_lead_sources()
[<LeadSource 'Website'>, <LeadSource 'Email'>]
>>> a.selectors.get_industry_types()
[<IndustryType 'Agriculture'>, <IndustryType 'Construction'>]
>>> a.selectors.get_business_types()
[<BusinessType 'Customer'>, <BusinessType 'Competitor'>]
>>> a.selectors.get_deal_payment_statuses()
[<DealPaymentStatus 'Paid'>, <DealPaymentStatus 'Unpaid'>]
>>> a.selectors.get_deal_products()
[<DealProduct 'Product 1'>, <DealProduct 'Product 2'>]
>>> a.selectors.get_deal_pipelines()
[<DealPipeline 'Sales Pipeline'>, <DealPipeline 'Renewal Pipeline'>]
>>> a.selectors.get_contact_statuses()
[<ContactStatus 'Open'>, <ContactStatus 'Closed'>]
>>> a.selectors.get_sales_activity_types()
[<SalesActivityType 'Call'>, <SalesActivityType 'Email'>]
>>> a.selectors.get_sales_activity_entity_types()
[<SalesActivityEntityType 'Contact'>, <SalesActivityEntityType 'Deal'>]
>>> a.selectors.get_sales_activity_outcomes()
[<SalesActivityOutcome 'Success'>, <SalesActivityOutcome 'Failure'>]
>>> a.selectors.get_lifecycle_stages()
[<LifecycleStage 'Lead'>, <LifecycleStage 'Customer'>]
You can efficiently filter through your contacts using the dynamic Filter API. Here's a quick guide on how to utilize it:
The filter accepts a dictionary with a key named filter_rule
. This key holds a list of filtering criteria, and each criterion is a dictionary with the following keys:
-
attribute: Specifies the attribute of a contact you want to filter on.
-
operator: Describes the kind of operation you'd like to perform. Examples include:
is_in
is_not_in
is_before
is_after
is_in_the_range
- ... (more operators can be added as needed)
-
value: The value you want the attribute to be compared against.
You can use sort and sort_type to sort the results. This method also returns a second boolean value that indicates whether there are more results to be fetched. This must be done manually with sorting as the API endpoint does not support pagination and shows only the first 100 results.
Here's a simple example that filters contacts based on their email address:
filter_data = [
{
"attribute": "contact_email.email",
"operator": "is_in",
"value": "[email protected]"
}
]
results = a.search.filter_contacts(filter_data)
print(results) # [<Contact 'James Sampleton'>]
To run the tests, you'll need to install the development dependencies:
pip install python-freshworks-crm[test]
Then, you can run the tests with:
pytest
Travis CI will run the tests against Python 3.6, 3.7, 3.8, 3.9., 3.10, and 3.11
You can also use Tox to run the tests against all supported versions of Python:
tox
Contributions are welcome! Bulk APIs as well as endpoints such as Files, Search (partially complete), Phone, .. are not implemented yet. Feel free to open a pull request.