Planhat logo

Introduction

Authentication

HTTP Response codes

Data standards

Bulk Upsert

User Activities

Methods

POST

Create Activity

POST

API Bulk

POST

Segment

Metrics

Methods

GET

Get dimension data

POST

Bulk

Planhat Models & API

Asset

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

Campaign

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

Churn

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

Company

POST

Create

PUT

Update

GET

Get by id

GET

Get list

GET

Get lean list

DELETE

Delete

PUT

BulkUpsert

Conversation

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

Custom Field

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

Enduser

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

Invoice

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

Issue

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

License

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

Note

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

NPS

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

Opportunity

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

Objective

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

Project

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

Sale

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

Task

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

Ticket

PUT

BulkUpsert

GET

Get list

DELETE

Delete

Time Entry

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

POST

Duplicate

Timesheet

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

User

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

Workspace

POST

Create

PUT

Update

GET

Get by id

GET

Get list

DELETE

Delete

PUT

BulkUpsert

Introduction

Welcome to Planhat's API! It will help you interact with Planhat whenever you need something that is not covered by one of our standard integrations.

The API has a few "open" endpoints that can be used to push data without the need to authenticate. This is ideal if all you need is to send some server side metrics, user activities, call logs, etc. If you need more than that, API keys will give you access to the full API which will let you do almost anything related to your data in Planhat.

Base URL

The base url for general API requests is:

https://api.planhat.com

The base url for user tracking and metrics is:

https://analytics.planhat.com

Rate Limits

Main API Endpoint

Planhat comes with a quota (soft limit) of 200 API calls per minute to our main api. The hard limit is 150 requests per second with bursts of up to 50 parallel requests.

Note: When modifying or creating multiple records in one request (large bulk operations), it is advised to execute the requests sequentially instead of in parallel. This will aid in maintaining data integrity.

For all bulk upsert operations, regardless the model there is an upper limit of 5,000 items per request.

Analytics Endpoint

The user tracking and metrics endpoints at analytics.planhat.com are not affected by these limits and can handle very high volumes of requests.

However, there is a hard limit of 32MB on the body of each post to Planhat, which typically corresponds to about 150,000 items.

API Alternatives

In some cases you may prefer to manage your Planhat data in some other way than over api. For those situations you have the following options:

  • Manually in the app
    It's easy to update any data in Planhat manually from within the app. You can create, update and remove whole objects or specific properties, bulk update is typically available as well.

  • Import / Export
    Another option to create and update data in Planhat is via xlsx file import. The file import corresponds to a bulkUpsert operation, creating items where they don't exist and updating if they already exist.

  • Integrations
    Planhat comes with many out of the box integrations to help sync data to and from CRMs such as Salesforce and Hubspot, Support Tools, Jira, NPS tools, etc. For those using Salesforce this is a common way to load companies and contacts (among other things) into Planhat.

  • Zapier
    There's a Zapier app for Planhat supporting the basic events and actions, for example to create companies, endusers and Notes. But using web requests really any api endpoint could be leveraged.

Authentication

Most end-points require authentication by an API Access Token that can be generated using Service Accounts under the Settings section in Planhat. Once a token is created, it will appear once and last forever. Make sure to copy and store it securely once generated.

To disable a token simply disable the Service Account, pause it, or remove the API Access Token from Service Account, the latter will permanently invalidate the token.

API Access Tokens (API keys) are static tokens that belong to a Service Account, meaning that whatever operation performed with this token, will appear as a Service Account action. It is possible to limit the access scope for an API Access Token by configuring permissions on the Service Account level.

The Token should be placed in the Authorization header in ONE of the following ways:

Authorization: Bearer {{apiAccessToken}}

or

Authorization: Basic [base64encode({{apiAccessToken}}:)]

Please note the colon after your token when using basic authentication.

HTTP Response codes

The Planhat API can respond with 5 possible HTTP codes: 200, 206, 400, 403 and 500.

For all the requests the API will respond with an object or string containing either the created/updated object or information to help you understand a possible error code. This is especially helpful for error codes where more information can help you understand what went wrong with the request.

  • 200: Ok code, this means the operation was successful.

  • 206: Partially Ok code, this means that some operations were successful but there are others with errors.

  • 400: Bad request, this means that you have a problem with the request payload, usually a required key is missing or it's duplicated.

  • 403: Permission error, this means that you don't have the right permissions to execute the operation. Check that your API token has the correct permissions.

  • 500: Server error, this means that there was an error on the Planhat side, try again and if you get the same 500 error please contact support.

Why are we always returning 200 for endpoints on our tracking & metrics base URL?

We have built these endpoints as “data sinks”. In this context, 200 means that we have successfully received the data for processing.

There are two key reasons we do not process the data in real-time and return a final result:

  • Firstly, due to the high data volumes and significant processing required for each event (e.g., analytics events need to be linked to the correct company, and an end user may be created and linked automatically in the process).

  • Secondly, because Metrics data is often sent by our Tracking Script, the request often goes directly from your enduser's browser to our endpoint, meaning there is no value in returning any errors.

Data standards

In order to keep consistency and make data the most reliable as possible, the API relies on a set of standards and rules to normalize data it takes in.

Some data a client sends can either be invalid or transformed to be accommodated and stored in a better shape.

Dates and time

All time-related data will be stored in UTC (offset 0) and API can tolerate non UTC values when handling Date/Time fields but they'll be represented in UTC time.

Futhermore, we provide three data types to work with dates:

  1. Day,

  2. Date and

  3. Date/Time.

All those types will be explained below.

Day

When a field has day type API expect it to be a number also known as UNIX Epoch, the value of it should be: the amount of days that have passed since Jan 1, 1970. For example, 19508 would represent the date May 31, 2023. This type of data can't tolerate time.

API can receive and handle ISO 8601 strings with or without time and timezone for day fields, although the end result will always be the number of days as explained above.

The following date strings are valid and will be converted correctly to the epoch representation:

Input Result
2023-05-01T00:00:00.000Z 19478
2023-05-02 19479
2023-05-03T05:00:00.000+02:00 19480

Date

Date type fields are stored as ISO 8601 date strings with zeroed time (T00:00.000Z). For example: 2023-05-01T00:00:00.000Z. When a time part is provided for this type of field it will be ignored.

The following date strings are valid and will have time zeroed when it's the case:

Input Result
2023-05-31T15:21:19.144Z 2023-05-31T00:00:00.000Z
2023-05-31T15:21:19.000+02:00 2023-05-31T00:00:00.000Z
2023-05-31 2023-05-31T00:00:00.000Z

Date/Time

Date/Time type fields are stored as ISO 8601 date strings with time in UTC. For example: 2023-05-01T21:53:22.634Z will be saved exactly like that. When timezone is provided we save the UTC representation of it

The following date strings are valid and will have time zeroed when it's the case:

Input Result
2023-05-31T15:21:19.144Z 2023-05-31T15:21:19.144Z
2023-05-31T15:21:19.000+02:00 2023-05-31T13:21:19.000Z
2023-05-31 2023-05-31T00:00:00.000Z

Bulk Upsert

The bulkupsert endpoints accept an array of objects to be upserted (created and/or updated).

To decide if an object will be inserted/created or updated, we first try to match it with exiting records in Planhat.

This matching can be based on a few different keyables (keys used by planhat to identify resources), typically:

  • _id (planhat native id).

  • sourceId (id from some external systems e.g. your CRM).

  • externalId (id in your own system).

The exact keyable available depends on the resource in question and these are listed under each section here in the docs.

Inserts

If no keyable is provided, or if the provided key doesn't match any existing record a new record will be created, assuming minimum required information is provided.

Generally very few fields are required, for example you are allowed to create a contact without providing first or last name.

The one exception is that most objects must be associated with a company profile in Planhat, which means that you'll need to provide a planhat companyId to create most objects.

Updates

To make an update you typically don’t need to provide companyId since the record already exists and is mapped to a company profile.

For an update to make sense it should contain at least one keyable to be used for matching and at least one property to be updated.

Properties that are used as keyable can only be updated by providing a higher priority key.

In general the hierarchy of keyables are: _id > sourceId > externalId.

Note: For the Enduser model the keyables hierarchy is:
_id > sourceId > externalId > email

Identical updates

In cases when the same update has to be applied to multiple documents, it is possible to use reduced payload.

Bulk upsert HTTP response codes

Bulk upserts can respond with 5 possible HTTP codes: 200, 206, 400, 403 and 500.


  • 200: Ok code, this means all the operation (create/update) were successful.

  • 206: Partial content, this means that some operations were successful but there are others with errors. Check the createdErrors/updatedErrors section on the response object for more information.

  • 400: Bad request, this means that you have a problem with the request payload, usually a required key is missing or it's duplicated. Check the createdErrors/updatedErrors section on the response object for more information.

  • 403: Permission error, this means that you don't have the right permissions to execute one or all the operations. Check the permissionErrors section on the response object for more information.

  • 500: Server error, this means that there was an error on the Planhat side, try again and if you get the same 500 error please contact support.

Response Object

The bulk upsert operation response with an object containing different properties each of which has some valuable information.

{
    "created": 0,
    "createdErrors": [],
    "insertsKeys": [],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [],
    "permissionErrors": []
}
  • created: Number of elements created.

  • createdErrors Array of error objects specifying the detail why a create intend failed.

  • insertsKeys Array of objects containing the inserted keys for each created element.

  • updated Number of elements updated.

  • updatedErrors Array of error objects specifying the detail why an update intend failed.

  • updatesKeys Array of objects containing the modified keys for each updated element.

  • nonupdates Number of element that were not updated.

  • modified Array of ids of updated elements.

  • upsertedIds Array of ids of created elements.

  • permissionErrors Array of objects containing the detail why a create or update intend failed due a permission problem.

Examples:

[{externalId: '123', firstName: 'luke'}]

This may find a contact with externalId equal to 123 and then set firstName to Luke.

But because externalId is used for matching it will not be updated.

To update externalId in this case you'll need to provide a matching sourceId or planhatId (_id)

[{_id: '507f191e810c19729de860ea', externalId: '999', firstName: 'luke'}]

If this were the same record as above (based on planhatId) then it would update externalId from 123 to 999.

Reduced payload to perform identical updates of multiple documents.

 {
"update": {"description": "New name for two companies"},
"key": "_id",
"ids": ["6262adb8b4f420096d599dd3", "6262adb8b4f420096d599db5"]
}

the same, but using sourceId

 {
"update": {"custom": {"contract value": 16}},
"key": "sourceId",
"ids": ["srcid-1", "srcid-2"]
}

Error Examples:

- Error due a missing required parameter

{
"created": 0,
"createdErrors": [
   {
       "item": {
           "fromDate": "2021-07-28T00:00:00.000Z",
           "toDate": "2022-07-28T00:00:00.000Z",
           "mrr": 100,
           "product": "Large License",
       },
       "err": [
           {
               "el": "companyId",
               "error": "Required Field"
           }
       ]
     }
  ],
  "insertsKeys": [],
  "updated": 0,
  "updatedErrors": [],
  "updatesKeys": [],
  "nonupdates": 0,
  "modified": [],
  "upsertedIds": [],
  "permissionErrors": []
}

- Error due a invalid object ID

{
   "created": 0,
   "createdErrors": [
       {
           "data": [
               {
                   "_id": "abcd",
                   "mrr": 200000
               }
           ],
           "err": "Cast to ObjectId failed for value \"abcd\" (type string) at path \"_id\" for model \"License\""
       }
   ],
   "insertsKeys": [],
   "updated": 0,
   "updatedErrors": [],
   "updatesKeys": [],
   "nonupdates": 0,
   "modified": [],
   "upsertedIds": [],
   "permissionErrors": []
}

- Error due a invalid type

{
   "created": 0,
   "createdErrors": [
       {
           "data": [
               {
                   "_id": "507f191e810c19729de860eb",
                   "mrr": "1500"
               }
           ],
           "err": [
               {
                   "mrr": "test",
                   "error": "Not valid type"
               }
           ]
       }
   ],
   "insertsKeys": [],
   "updated": 0,
   "updatedErrors": [],
   "updatesKeys": [],
   "nonupdates": 0,
   "modified": [],
   "upsertedIds": [],
   "permissionErrors": []
}

Note: For all bulk upsert operations, regardless the model there is an upper limit of 5,000 items per request.

User Activities

Keeping track of your endusers activity is an important part of the Customer Success effort. Getting started involves only two steps.

  1. Decide a few events (user actions) that you want to track.

  2. Decide how you want to get that information from your users into Planhat.

What events to track

The most obvious events are probably logins and page-views. Logins tend to be fairly inaccurate since it depends a lot on user behavior, some people login and logout a lot, others keep their sessions open. Page views is likely a better metric. Even though page views have relatively weak correlation with the value created, it does give you a general sense of activity level, and it’s great to understand when a given user/customer was last active.

In addition to page views you can probably find even more meaningful metrics such as "did an advanced search", "created a campaign", "added a team-member" etc. The exact metrics will obviously depend on the service you provide but 2-3 such events should be more than enough to get started. Add a weight if you want to indicate that some actions are more important/valuable than others.

How to track

There are 4 different ways of getting your enduser activity into Planhat.

  1. API (real time)

  2. Planhat Tracking Script

  3. Segment

  4. API Bulk

Note: Regardless your location the base URL for User Tracking events is always https://analytics.planhat.com

Planhat Tracking Script

Your second option is to add a small tracking script to your application. Like most other tracking scripts, it’s a tiny snippet (< 1 kb) that you place just before the Head closing tag on your web app. The snippet will asynchronously load the tracking script to send data directly from your users to Planhat. The tracking script will not slow down your app or have any noticeable impact on your users.

To get your Tenant UUID please visit the Developer section in Planhat App.

<script type="text/javascript"> var plantrack=plantrack||[];!function(){for(var t=["init","identify","track"], a=function(t){ return function(){plantrack.push([t].concat(Array.prototype.slice.call(arguments,0)))}},n=0;n<t.length;n++)plantrack[t[n]]=a(t[n])}(),plantrack.load=function(t,a){plantrack._endpoint=t,a&&plantrack.init(a); var n=document.createElement("script");n.type="text/javascript",n.async=!0, n.src="https://app.planhat.com/analytics/plantrack.min.js"; var r=document.getElementsByTagName("script")[0];r.parentNode.insertBefore(n,r)}; plantrack.load("https://analytics.planhat.com/analytics/{{tenantUUID}}"); </script>

Then in your application, identify users as they login by calling

plantrack.identify(USER_ID, {EXTRA_INFO}).
  • USER_ID - corresponds to externalId of the EndUser in Planhat. Note! If users can be identified only by email, in that case simply pass "null" as a first argument to plantrack.identify but make sure to specify the user email property in EXTRA_INFO.

  • EXTRA_INFO - the way to specify additional information to identify (or create if it's allowed by your settings) a user. Supported fields are: "email", "name", "companyExternalId", "assetExtId", "projectExtId". This information is saved in a cookie ("_plantrack") and used to identify the user in subsequent requests.

plantrack.identify(USER_ID, { email: ojpsoi57pzn@mycompany.com, name: {{userName}}, companyExternalId: "mycompany-ext-id" });

After that, when the event you would like to track happens simply call plantrack.track(ACTION, [EXTRA_INFO]) from your app, for example:

plantrack.track("created a new project", {"weight": 7});

If you want to add more information, do it in a info property inside the JSON object of the second argument of the function call, like this:

plantrack.track("created a new project", {"weight": 7, "info": {"prop": "value", "prop2": "value2"} });

User information stored in the cookie (most importantly USER_ID or email passed in EXTRA_INFO) will automatically be added to all tracking requests.

Create ActivityPOST

https://analytics.planhat.com/analytics/{{tenantUUID}}

In table below you can get some of the field description for request.

Property Required Type Description
name
string Full name of the contact/user. This property is not required but it's recommended.
email
string The contact’s email.
externalId
string The contact’s id (user_id) in your system (required if email not specified).
companyExternalId
string Typically this is the account/customer id in your own system and can be very helpful in mapping new enduser to correct customer profile in Planhat.
action
string Parameter describing the user activity. Typically on the form Action + Object, for example "Sent Invoice", but it could also be just "Login" or something else that describes the activity.
assetExtId
string Asset id in your system that corresponds to externalId of Asset in Planhat.
projectExtId
string Project id in your system that corresponds to externalId of Project in Planhat.
info
object An optional valid JSON object where you can specify additional information related to the event. If the user activity was "purchased a gift card" then it might make sense to add info about the price, or product category.
passive
boolean Pass true to track event as passive which will NOT affect on contact/user Last Seen/Active date, Beat & Experience. Passive events will not show in the Company Profile “Overview” tab under “Recently Active”, but will show in “Recent Activities” both in the Company Profile “Usage” tab and the End User Profile “Overview” tab.
weight
number Optional numeric value lets you indicate that some actions are more important/valuable than others.

Creating users automatically

If we receive a call (Identify or Track) for a user that doesn’t yet exist in Planhat, and the request contains a customer id, then Planhat will automatically create a new user for you. This is highly recommended unless you create users in Planhat automatically through the API. If you’re providing an email address it is also likely that Planhat will be able to map a new enduser to the correct customer account with sufficient accuracy to avoid manual confirmation, however most Segment set-ups rely not on email but user id.

When a new end user is auto-created by Planhat, their First & Last names, External ID and Email will all be populated automatically if provided in the call to the analytics endpoint.

Updating User Properties

If an end user already exists in Planhat, and any of the above properties (First Name, Last Name, External ID, Email) are sent, the End User will only be updated if they do not already have a value for the relevant property. If, however, the end user already has a value, the property will not be updated by the call.

Example Request

curl --location -g --request POST 'https://analytics.planhat.com/analytics/{{tenantUUID}}' \
--header 'Content-Type: application/json' \
--data-raw '{
    "email": "ivars@planhat.com",
    "action": "Logged in",
    "externalId": "ojpsoi57pzn",
    "companyExternalId": "Planhat-81ock9l81wl",
    "weight": 1,
    "info": {
        "index": 20,
        "theme": "Blue"
    }
}'

Example Response

200 OK

OK

API BulkPOST

https://analytics.planhat.com/analytics/bulk/{{tenantUUID}}

Limits

The count property for User Activities cannot handle a value greater than 300 (0 ≤ count ≤ 300). If a value over 300 is sent in the count property, then the endpoint will receive a count of 1 instead of the value sent.

Property Required Type Description
name
string Full name of the contact/user.
email
string The contact’s email.
euExtId
string The contact’s id (user_id) in your system (required if email not specified).
cId
objectId Typically the account/customer id in your system, the same id have to be present as externlId in Planhat.This is required to automatically add new endusers to the correct customer profile in Planhat. If left out, a Customer Success Rep will manually have to assign new endusers to the appropriate customer, or Planhat will make a qualified guess when certain enough it will be correct.
date
string Pass a valid ISO date string to specify the date of the event. If none is provided we will use the time the request was received.
action
string Parameter describing the user activity. Typically on the form Action + Object, for example "Sent Invoice", but it could also be just "Login" or something else that describes the activity. This parameter is optional, but highly recommended and required if you want the appear in the event stream.
assetExtId
string Asset id in your system that corresponds to externalId of Asset in Planhat.
projectExtId
string Project id in your system that corresponds to externalId of Project in Planhat.
count
integer Indicate if the event happened multiple times, for example if the import cover past 24 hours and a given user logged in twice during this period, you could send it as one row and instead set the count to 2, of course they'd get the exact same date, but mostly this is not an issue. Defaults to one (1).
passive
boolean Pass true to track event as passive which will NOT affect on contact/user Last Seen/Active date, Beat & Experience. Passive events will not show in the Company Profile “Overview” tab under “Recently Active”, but will show in “Recent Activities” both in the Company Profile “Usage” tab and the End User Profile “Overview” tab.
weight
number Optional numeric value lets you indicate that some actions are more important/valuable than others.

Example Request

curl --location -g --request POST 'https://analytics.planhat.com/analytics/bulk/{{tenantUUID}}' \
--header 'Content-Type: application/json' \
--data-raw '[
    {
        "name": "Ivars Mucenieks",
        "email": "ivars@planhat.com",
        "cId": "abc123",
        "action": "Logged in",
        "weight": 1,
        "count": 1
    },
    {
        "name": "Ivars Mucenieks",
        "email": "ivars@planhat.com",
        "cId": "abc123",
        "action": "Sent Invoice",
        "weight": 1,
        "count": 1
    }
]'

Example Response

200 OK

OK

SegmentPOST

https://analytics.planhat.com/dock/segment

As a third option, Segment can be used to send User Events (user tracking data) to Planhat. When we receive an event from Segment, we use the "user id" and/or email to to know which Planhat user the event refers to. The events are then displayed in Planhat both on user and company level. When presented in text form, we display it as [user name] + [event]. Preferably use event names on the form "verb + noun". For example "downloaded a report" is a better event name then "report_dowloaded".

Notes:

  • Segment is a service with it’s own license cost.

  • Segment endpoint uses Basic Auth passing the tenantUUI as username.

Creating users automatically

If we receive a call (Identify or Track) for a user that doesn’t yet exist in Planhat, and the request contains a customer id, then Planhat will automatically create a new user for you. This is highly recommended unless you create users in Planhat automatically through the API. If you’re providing an email address it is also likely that Planhat will be able to map a new enduser to the correct customer account with sufficient accuracy to avoid manual confirmation, however most Segment set-ups rely not on email but user id.

When a new end user is auto-created by Planhat, their First & Last names, External ID and Email will all be populated automatically if provided in the call to the analytics endpoint.

Updating User Properties

If an end user already exists in Planhat, and any of the above properties (First Name, Last Name, External ID, Email) are sent, the End User will only be updated if they do not already have a value for the relevant property. If, however, the end user already has a value, the property will not be updated by the call.

Ignored events

Events will be completely ignored when any of the following conditions are met.

Request is not of type "Identify" or "Track" Both User Id and Email are missing

Unassigned users

If we receive a call (Identify or Track) for a user that doesn’t yet exist in Planhat, and the request does NOT contain information about which customer it relates to, and we have no other way to relate the user to a customer with reasonable confidence, then it will be discarded but added a report in Planhat to help you identify potential issues with the integration. Typically this may happen when users that loggedin (with a persistent session) before the integration was activated. Events from these users will end up in the log, and eventually as the user gets logged out and login again it’ll start working as expected.

Property Required Type Description
type
string Type of segments should be (Identify or Track).
traits
object Object of some data ( email or user id is required)

Example Request

curl --location -g --request POST 'https://analytics.planhat.com/dock/segment' \
-u {{tenantUUID}} \
-k \
--header 'Content-Type: application/json' \
--data-raw '{
    "type": "identify",
    "traits": {
        "name": "Ivars Mucenieks",
        "email": "ivars@planhat.com",
        "companyId": "ABCDE"
    }
}'

Example Response

200 OK

OK

Metrics

https://analytics.planhat.com/dimensiondata

While User Activity says a lot about user engagement, it doesn't always reflect the value created. Dimension Data is a set of model level metrics (Company by default) to understand how well your customers are doing, and the value they get out of your service.

Depending on what metrics you pick you might either want to send it as it happens or once a day. Number of user logins for example. You could either send a post everytime a user logs in, or you could save logins at your end and send the total count for the day to planhat once a day. Typically the more granular data you send to planhat, the more options you’ll have to crunch that data later.

Note: The end point accepts either a single object or an array, which can be useful if you run a batch update with some interval, or if you initially would like to load historical data.

Note: regardless your location the base URL for User Tracking events is always https://analytics.planhat.com.

In table below you can get some of the field description for request.

Property Required Type Description
dimensionId
string Any string without spaces or special characters. If you're sending "Share of Active Users" a good dimensionId might be "activeusershare". It's not displayed in Planhat but will be used when building Health Metrics in Planhat.
value
number The raw (number) value you would like to set.
externalId
string This is the model (company by default) external id in your systems. For this to work the objects in Planhat will need to have this externalId set.
model
string Company (default), EndUser, Asset and Project models are supported
date
string Pass a valid ISO format date string to specify the date of the event. In none is provided we will use the time the request was received.

Get dimension dataGET

https://api.planhat.com/dimensiondata

When fetching dimension data there are some options that can be used via query params:

  • cId: Id of company.

  • dimid: Id of the dimension data.

  • from: Days format interger representing the start day period (Days since January 1, 1970, Unix epoch).

  • to: Days format interger representing the end day period (Days since January 1, 1970, Unix epoch).

  • limit: Limit the list length.

  • offset: Start the list on a specific integer index.

Example Request

curl --location -g --request GET 'https://api.planhat.com/dimensiondata?cId=611547eebcdda93cc7622958&dimid=testdim&from=18858&to=18880&limit=3' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "611ef080ff18a32f886e40ae",
        "dimensionId": "testdim",
        "time": "2021-08-20T00:00:00.000Z",
        "value": 5,
        "model": "Company",
        "parentId": "611547eebcdda93cc7622958",
        "companyId": "611547eebcdda93cc7622958",
        "companyName": "Tenet"
    },
    {
        "_id": "611ef080eb75161dc055880a",
        "dimensionId": "testdim",
        "time": "2021-08-20T00:00:00.000Z",
        "value": 4,
        "model": "Company",
        "parentId": "611547eebcdda93cc7622958",
        "companyId": "611547eebcdda93cc7622958",
        "companyName": "Tenet"
    },
    {
        "_id": "611ef080b067346dfd91ad22",
        "dimensionId": "testdim",
        "time": "2021-08-20T00:00:00.000Z",
        "value": 15,
        "model": "Company",
        "parentId": "611547eebcdda93cc7622958",
        "companyId": "611547eebcdda93cc7622958",
        "companyName": "Tenet"
    }
]

Bulk Insert MetricsPOST

To push dimension data into Planhat is is required to specify the Tenant Token (tenantUUID) in the request URL. This token is a simple uui identifier for your tenant and it can be found in the Developer module under the Tokens section.

Example Request

curl --location -g --request POST 'https://analytics.planhat.com/dimensiondata/{{tenantUUID}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
    {
        "dimensionId": "flightTime",
        "value": 1,
        "externalId": "airbus",
        "model": "Company"
    },
    {
        "dimensionId": "debtRatio",
        "value": 5.2,
        "externalId": "a320",
        "model": "EndUser"
    }
]'

Example Response

200 OK

'{
    "processed": 2,
    "errors": []
}'

Planhat Models & API Endpoints

Planhat provides a set of models to which you can interact with via API.

The main properties, options and features are listed below but some models have more properties and functionalities than the ones provided on this documentation. If you don't find something in specific here, please contact support for help.

Array Operations

Standard Arrays in Planhat, such as any System or Custom Multipicklist field on any object, allow for simple Add, Replace and Remove operations as follows:

  • Add will append the new value(s) to the existing array.

  • Replace will input the new value(s) instead of the existing array

  • Remove will remove the new value(s) if present in the existing array

The syntax for applying such array operations is:

{
  "property":   { 
      "operation": "insert|replace|remove", 
      "data": ["value1", "value2", "value3"]
  }
};

For example, the following payload would update a custom field called "Industry" by removing the "Healthcare" value:

{
  "custom": { 
      "Industry":   {
              "operation": "remove", 
              "data": ["Healthcare"]
      }
  }
};

Asset

https://api.planhat.com/assets

Assets in Planhat can represent many different things depending on your use case. It could be drones, if you're selling a drone tracking product, or it could be instances of your product in cases where a single customer can run multiple instances of your product in parallel. Assets could also represent your different products.

More generally, Assets are "nested objects" for which you may want to track usage separately, but don't need to treat them as separate customers with individual contacts, conversations, etc.

Property Required Type Description
_id
objectId Planhat identifier.
name
string Asset name.
companyId
objectId Related company id (planhat identifier).
companyName
string Company name. (Autogenerated).
externalId
string The asset id in your own system.
sourceId
string The asset id from an integration (Sales Force, Hubspot, etc).
custom
object Custom object with your custom properties.

Create AssetPOST

https://api.planhat.com/assets

To create an asset it's required define a name and a valid companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

Example Request

curl --location -g --request POST 'https://api.planhat.com/assets' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
    "name": "Asset name",
    "companyId": "60df26bfa259250cba9cf816",
    "externalId": 1234,
    "sourceId": "sfc-1234",
    "custom": {
        "field": "custom field"
    }
}'

Example Response

200 OK

'{
    "_id": "60faeda853b8f717ebe36146",
    "name": "Asset name",
    "companyId": "60df26bfa259250cba9cf816",
    "externalId": "1234",
    "sourceId": "sfc-1234",
    "custom": {
        "field": "custom field"
    },
    "companyName": "Test Company"
}'

Update AssetPUT

https://api.planhat.com/assets/:_id

To update an asset it's required to pass the _id in the request URL as a parameter.

Alternately it’s possible to update using the asset externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/assets/extid-{{externalId}}

or

https://api.planhat.com/assets/srcid-{{sourceId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/assets/60faeda853b8f717ebe36146' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "name": "Asset name 2"
    }'

Example Response

200 OK

'{
    "_id": "60ff061d681a6b4da9248694",
    "name": "Asset name 2",
    "companyId": "60df26bfa259250cba9cf816",
    "externalId": "12323",
    "companyName": "Test Company"
    "__v": 0,
    "sourceId": "12323"
}'

Get Assets by IDGET

https://api.planhat.com/assets/:_id

To get a specific asset it's required to pass the _id in the request URL as a parameter.

Alternately it's possible to get an asset using its externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/assets/extid-{{externalId}}

or

https://api.planhat.com/assets/srcid-{{sourceId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/assets/5ffcce42ad67267f66741147' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "5ffcce42ad67267f66741147",
    "companyId": "56bccdf554d64d837d01be9d",
    "companyName": "Tenet",
    "name": "Gold asset",
    "__v": 0,
    "custom": {
        "Comments": "",
        "Main Product": [
            "Recipe Database"
        ],
        "Days in Phase": 0,
        "NRR30": 0,
        "t123": "undefined",
        "Logins": 0,
        "Overall Outlet Health": 4,
        "Kary FF": null
    },
    "externalId": "4467",
    "usage": {
        "6053b728fa96fa0262729a3d": 0,
        "6053ba62ca3eaf023a54d268": 0
    }
}'

Get Assets ListGET

https://api.planhat.com/assets

When fetching multiple assets there are some options that can be used via query params:

  • companyId: Filter using company id.

  • limit: Limit the list length. Default as 100, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/assets?limit=2&offset=0&sort=-name&select=_id,name,companyId' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "60fb0869694ea374023924cb",
        "companyId": "56bccdf554d64d837d01be4a",
        "name": "ZPA Product"
    },
    {
        "_id": "601c0a253e5ed41388982528",
        "companyId": "6011889f52181c5640bf41ba",
        "name": "Trello.io"
    }
]'

Delete AssetDELETE

https://api.planhat.com/assets/:_id

To delete an asset it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/assets/60faeda853b8f717ebe36146' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert AssetsPUT

https://api.planhat.com/assets

To create an asset it's required define a name and a valid companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

To update an asset it is required to specify in the payload one of the following keyables, listed in order of priority: _id, sourceId, externalId.

Since this is a bulk upsert operation it's possible create and/or update multiple assets with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/assets' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
        {
            "name": "Asset name",
            "companyId": "60df26bfa259250cba9cf816",
            "externalId": 1234,
            "sourceId": "sfc-1234",
            "custom": {
                "field": "custom field"
            }
        },

        {
            "name": "Asset name 2",
            "companyId": "60df26bfa259250cba9cf816",
            "externalId": 1235,
            "sourceId": "sfc-1235",
            "custom": {
                "field": "custom field"
            }
        }
    ]'

Example Response

200 OK

'{
    "created": 2,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "60fde9ad57df5b411cf39be5",
            "sourceId": "sfc-1234",
            "externalId": "1234"
        },
        {
            "_id": "60fde9ad57df5b411cf39be6",
            "sourceId": "sfc-1235",
            "externalId": "1235"
        }
    ],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [
        "60fde9ad57df5b411cf39be5",
        "60fde9ad57df5b411cf39be6"
    ],
    "permissionErrors": []
}'

Campaign

https://api.planhat.com/campaigns

Manage campaigns you are running inside companies, e.g., to drive adoption or to deepen stakeholder relations.

Property Required Type Description
_id
objectId Planhat identifier.
name
string Campaign name.
campaignPurpose
string Campaign purpose.
companyId
objectId Related company id (planhat identifier).
companyName
string Company name. (Autogenerated).
externalId
string The campaign id in your own system.
sourceId
string The campaign id from an integration (Sales Force, Hubspot, etc).
custom
object Custom object with your custom properties.

Create CampaignPOST

https://api.planhat.com/campaigns

To create an campaign it's required define a name and a valid companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

Example Request

curl --location -g --request POST 'https://api.planhat.com/campaigns' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
    "name": "Campaign name",
    "campaignPurpose": "Campaign purpose",
    "companyId": "60df26bfa259250cba9cf816",
    "externalId": 1234,
    "sourceId": "sfc-1234",
    "custom": {
        "field": "custom field"
    }
}'

Example Response

200 OK

'{
    "_id": "60faeda853b8f717ebe36146",
    "name": "Campaign name",
    "campaignPurpose": "Campaign purpose",
    "companyId": "60df26bfa259250cba9cf816",
    "externalId": "1234",
    "sourceId": "sfc-1234",
    "custom": {
        "field": "custom field"
    },
    "companyName": "Test Company"
}'

Update CampaignPUT

https://api.planhat.com/campaigns/:_id

To update an campaign it's required to pass the _id in the request URL as a parameter.

Alternately it’s possible to update using the campaign externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/campaigns/extid-{{externalId}}

or

https://api.planhat.com/campaigns/srcid-{{sourceId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/campaigns/60faeda853b8f717ebe36146' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "name": "Campaign name 2"
    }'

Example Response

200 OK

'{
    "_id": "60ff061d681a6b4da9248694",
    "name": "Campaign name 2",
    "campaignPurpose": "Campaign purpose 2",
    "companyId": "60df26bfa259250cba9cf816",
    "externalId": "12323",
    "companyName": "Test Company"
    "__v": 0,
    "sourceId": "12323"
}'

Get Campaigns by IDGET

https://api.planhat.com/campaigns/:_id

To get a specific campaign it's required to pass the _id in the request URL as a parameter.

Alternately it's possible to get an campaign using its externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/campaigns/extid-{{externalId}}

or

https://api.planhat.com/campaigns/srcid-{{sourceId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/campaigns/5ffcce42ad67267f66741147' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "5ffcce42ad67267f66741147",
    "companyId": "56bccdf554d64d837d01be9d",
    "companyName": "Tenet",
    "name": "Gold campaign",
    "campaignPurpose": "Campaign purpose",
    "__v": 0,
    "custom": {
        "Comments": "",
        "Main Product": [
            "Recipe Database"
        ],
        "Days in Phase": 0,
        "NRR30": 0,
        "t123": "undefined",
        "Logins": 0,
        "Overall Outlet Health": 4,
        "Kary FF": null
    },
    "externalId": "4467",
    "usage": {
        "6053b728fa96fa0262729a3d": 0,
        "6053ba62ca3eaf023a54d268": 0
    }
}'

Get Campaigns ListGET

https://api.planhat.com/campaigns

When fetching multiple campaigns there are some options that can be used via query params:

  • companyId: Filter using company id.

  • limit: Limit the list length. Default as 100, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/campaigns?limit=2&offset=0&sort=-name&select=_id,name,campaignPurpose,companyId' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "60fb0869694ea374023924cb",
        "companyId": "56bccdf554d64d837d01be4a",
        "name": "ZPA Product",
        "campaignPurpose": "Campaign purpose"
    },
    {
        "_id": "601c0a253e5ed41388982528",
        "companyId": "6011889f52181c5640bf41ba",
        "name": "Trello.io",
        "campaignPurpose": "Campaign purpose 2",
    }
]'

Delete CampaignDELETE

https://api.planhat.com/campaigns/:_id

To delete a campaign it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/campaigns/60faeda853b8f717ebe36146' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert CampaignsPUT

https://api.planhat.com/campaigns

To create a campaign it's required define a name and a valid companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

To update a campaign it is required to specify in the payload one of the following keyables, listed in order of priority: _id, sourceId, externalId.

Since this is a bulk upsert operation it's possible create and/or update multiple campaigns with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/campaigns' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
        {
            "name": "Campaign name",
            "campaignPurpose": "Campaign purpose",
            "companyId": "60df26bfa259250cba9cf816",
            "externalId": 1234,
            "sourceId": "sfc-1234",
            "custom": {
                "field": "custom field"
            }
        },

        {
            "name": "Campaign name 2",
            "campaignPurpose": "Campaign purpose 2",
            "companyId": "60df26bfa259250cba9cf816",
            "externalId": 1235,
            "sourceId": "sfc-1235",
            "custom": {
                "field": "custom field"
            }
        }
    ]'

Example Response

200 OK

'{
    "created": 2,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "60fde9ad57df5b411cf39be5",
            "sourceId": "sfc-1234",
            "externalId": "1234"
        },
        {
            "_id": "60fde9ad57df5b411cf39be6",
            "sourceId": "sfc-1235",
            "externalId": "1235"
        }
    ],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [
        "60fde9ad57df5b411cf39be5",
        "60fde9ad57df5b411cf39be6"
    ],
    "permissionErrors": []
}'

Churn

https://api.planhat.com/churn

Each time one of your customers churns or downgrades you can add a specific log about this. Mostly this "churn log" is added manually by the CSM from within Planhat, but there may also be times when you want to add it over API, for example if you're capturing information about downgrades and churn natively in-app in your own platform and want to send that over to Planhat.

The churn logs in Planhat typically contain the reasons for the churn, the value, date etc. It's important to note though that it doesn't affect actual revenue numbers and KPIs such as churn rate, renewal rate etc, on it's own. Those calculations are entirely based on underlying license data.

Property Required Type Description
_id
objectId Planhat identifier.
companyId
objectId Related company id (planhat identifier).
churnDate
string ISO date indication when the churn takes effect.
currency
string The currency code, for example "USD".
value
number Monthly churn value.
arr
number Annual churn value.
reasons
array String array with name of the churn reasons to log this churn with that certain reason.
comment
string Comments or description for the churn.
snoozed
boolean Field only used by the systeam. (Autogenerated).
products
array Array of product objects. (Autogenerated).
companyName
string Company name. (Autogenerated).
sourceId
string The churn id from an integration (Sales Force, Hubspot, etc).
custom
object Custom object with your custom properties.

Create ChurnPOST

https://api.planhat.com/churn

To create a churn the only real value that is required is the companyId but it doesn't make much sense to have a churn just with a companyId, that is why we suggest specify also a value.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

Example Request

curl --location -g --request POST 'https://api.planhat.com/churn' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
    "churnDate": "2021-07-28",
    "reasons": [
        "Change in leadership",
        "Budget cuts"
    ],
    "value": 2000,
    "currency": "USD",
    "comment": "This account could return in the future",
    "companyId": "61006bc89a3e0b702ed8ea49"
}'

Example Response

200 OK

'{
    "snoozed": false,
    "products": [],
    "reasons": [
        "Change in leadership",
        "Budget cuts"
    ],
    "_id": "6101d1f072c0e0884d5e8f57",
    "churnDate": "2021-07-28T00:00:00.000Z",
    "value": 2000,
    "currency": "USD",
    "comment": "This account could return in the future",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "companyName": "Tenet",
    "__v": 0
}'

Update ChurnPUT

https://api.planhat.com/churn/:_id

To update a churn it's required to pass the _id in the request URL as a parameter.

Alternately it’s possible to update using the churn sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/churn/srcid-{{sourceId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/churn/6101d1f072c0e0884d5e8f57' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "value": 25000
    }'

Example Response

200 OK

'{
    "_id": "6101d1f072c0e0884d5e8f57",
    "snoozed": false,
    "products": [],
    "reasons": [
        "Change in leadership",
        "Budget cuts"
    ],
    "churnDate": "2021-07-28T00:00:00.000Z",
    "value": 25000,
    "currency": "USD",
    "comment": "This account could return in the future",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "companyName": "Tenet",
    "__v": 0
}'

Get Churn by IDGET

https://api.planhat.com/churn/:_id

To get a specific churn it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request GET 'https://api.planhat.com/churn/58cc4b769649cb337f9d59a9' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "58cc4b769649cb337f9d59a9",
    "licenseId": "56bccdf554d64d837d01be5b",
    "reasons": [
        "Missing features"
    ],
    "comment": "",
    "replacement": "",
    "companyId": "56bccdf554d64d837d01be59",
    "__v": 0,
    "companyName": "t2",
    "churnDate": "2017-07-29T00:07:49.000Z",
    "products": [
        "License Fee"
    ],
    "currency": "USD",
    "value": 3979.1666666666665,
    "custom": null
}'

Get Churn ListGET

https://api.planhat.com/churn

When fetching multiple churn there are some options that can be used via query params:

  • List of churn with downgrades: /churn?downgrades=only

  • List of churn without downgrades: /churn?downgrades=excluded

  • List of churn by time range: /churn?from=2020-01-01&to=2020-01-10 (Dates in ISO format).

  • companyId: Filter using company id.

  • limit: Limit the list length. Default as 100, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/churn' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "583b8d15f075d7b52cd392e2",
        "licenseId": "56bccdf654d64d837d01d812",
        "reasons": [
            "Customer got acquired"
        ],
        "comment": "notes..",
        "replacement": "",
        "companyId": "56bccdf554d64d837d01be6f",
        "__v": 0,
        "companyName": "CBS",
        "churnDate": "2020-10-10T00:00:00.000Z",
        "products": [
            "License Fee"
        ],
        "currency": "USD",
        "value": 5000,
        "custom": null,
        "lastUpdated": "2020-10-08T15:09:29.982Z"
    },
    {
        "_id": "58cc4b769649cb337f9d59a9",
        "licenseId": "56bccdf554d64d837d01be5b",
        "reasons": [
            "Missing features"
        ],
        "comment": "",
        "replacement": "",
        "companyId": "56bccdf554d64d837d01be59",
        "__v": 0,
        "companyName": "t2",
        "churnDate": "2017-07-29T00:07:49.000Z",
        "products": [
            "License Fee"
        ],
        "currency": "USD",
        "value": 3979.1666666666665,
        "custom": null
    },
]'

Delete ChurnDELETE

https://api.planhat.com/churn/:_id

To delete a churn it's required to pass _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/churn/6101d1f072c0e0884d5e8f57' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert ChurnPUT

https://api.planhat.com/churn

To create a churn it's required define a companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

To update a churn it is required to specify in the payload one of the following keyables, listed in order of priority: _id, sourceId.

Since this is a bulk upsert operation it's possible create and/or update multiple projects with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/churn' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
        {
            "churnDate": "2021-07-29",
            "reasons": [
                "Change in leadership",
                "Budget cuts"
            ],
            "value": 2500,
            "currency": "USD",
            "comment": "This account could return in the future",
            "companyId": "61006bc89a3e0b702ed8ea49"
        },
        {
            "_id": "60e5d4dabe45b22e8e97940c",
            "comment": "New communication with the CEO, the could return next year"
        }
    ]'

Example Response

200 OK

'{
    "created": 1,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "6101d5dc72c0e0884d5e9002"
        }
    ],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [
        "6101d5dc72c0e0884d5e9002"
    ],
    "permissionErrors": []
}'

Company

https://api.planhat.com/companies

Companies ("accounts"), are your customers. Depending on your business these might be agencies, schools, other businesses or something else. Companies can also be your previous customers and potentially future customers (prospects).

The company object is one of the most central in Planhat since most other objects relate to it, and it's frequently used to filter out other information, such as endsuers, licenses, notes etc.

In Planhat it is possible have a hierarchical structure for the companies, meaning that they can be grouped into organizations with parents and children in a tree like structure.

Property Required Type Description
_id
objectId Planhat identifier.
name
string Name of the company.
owner
objectId The the planhat user id of the Account Manager / Success Manager to whom this company belongs. See the User model section below for info about how to get the user ids.
coOwner
objectId A potential co-pilot, in case a single owner isn’t enough. See the User model section below for info about how to get the user ids.
externalId
string The company id in your own system (will help us match other data you send over such as user activities, custom metrics etc). Not strictly required but if you’ve read this far it’s most likely something you need.
sourceId
string The company id from an integration (Sales Force, Hubspot, etc).
phase
string Each company can be assigned a lifecycle phase in Planhat. It’s a text string that should match the name of one of the phases in Planhat. If the name doesn’t match any of the phases in Planhat it will still be saved. (Autogenerated).
status
string Status of company should be one of the list (prospect,coming,customer,canceled,lost). (Autogenerated).
domains
array Array of strings of company domains based on endusers emails domains. (Autogenerated).
h
integer Health number. (Autogenerated).
hProfile
objectId ObjectId of the current health profile. (Autogenerated).
csmScore
integer CSM Score from 1 to 5. (Autogenerated).
mr
number Current MRR plus the sum of NRR over the past 30 days. (Autogenerated).
nrr30
number NRR (Sale) value Last 30. (Autogenerated).
mrrTotal
number Accumulated MRR. (Autogenerated).
nrrTotal
number Accumulated non-recurring revenue. (Autogenerated).
mrTotal
number Accumulated Revenue (mrrTotal + nrrTotal). (Autogenerated).
description
string Company description.
address
string Company physical address.
country
string Company country.
city
string Company city.
zip
string Company postal code.
phonePrimary
string Company primary phone number.
web
string Company web site url.
customerFrom
string Start date of the active license. (Autogenerated).
customerTo
string Renewal date of the active license. (Autogenerated).
alerts
array Array of alerts notifications. (Autogenerated).
tags
array Array of tags.
products
array Array of string containing the products. (Autogenerated).
licenses
array Array of objects containing the licenses. (Autogenerated).
sales
array Array of objects containing the sales. (Autogenerated).
createDate
string Creation date. (Autogenerated).
lastUpdated
string Last update date. (Autogenerated).
lastRenewal
string Last renewal date. (Autogenerated).
renewalDaysFromNow
number Number of days before the renewal. (Autogenerated).
lastActive
string Last active date. (Autogenerated).
followers
array Array of followers (users) ids.
usage
map Object containing the usage data. (Autogenerated).
lastTouch
string Last touch date. (Autogenerated).
lastTouchType
string Last touch type. (Autogenerated).
lastTouchByType
object Object containing the info of the last contact. (Autogenerated).
nextTouch
string Date for the next schedule touch. (Autogenerated).
phaseSince
string Date since the last phase change. (Autogenerated).
csmScoreLog
array Array of object containing the csm change log. (Autogenerated).
features
array Array of object containing success units. (Autogenerated).
sunits
map Map object containing success units. (Autogenerated).
shareable
object Object containing the sharable information. (Autogenerated).
lastActivities
array Array of object containing the last activities. (Autogenerated).
documents
array Array of object the uploaded documents. (Autogenerated).
orgRootId
objectId Planhat ID of the top company on the organization tree. All its children must refer to the same root ID.(Autogenerated).
orgPath
string String containing chain of objectIds joined by commas. These describe a hierarchical order of companies within the organization. The leftmost is the parent of the organization, and the last one is the direct parent of the current company. (Autogenerated).
mrr
number Monthly company value. (Autogenerated).
arr
number Annual company value. (Autogenerated).
orgMrr
number MRR of the group. (Autogenerated).
orgArr
number ARR of the group. (Autogenerated).
renewalMrr
number Renewal monthly value. (Autogenerated).
renewalArr
number Renewal annual value. (Autogenerated).
orgMrrTotal
number Total Group MRR. (Autogenerated).
orgArrTotal
number Total Group ARR. (Autogenerated).
orgHealthTotal
number Total group health. (Autogenerated).
orgLevel
number Level of the current company inside the organization. (Autogenerated).
orgUnits
number Number of child companies. (Autogenerated).
custom
object A flexible object with custom data.

Create CompanyPOST

https://api.planhat.com/companies

To create a company it's required define a name.

Example Request

curl --location -g --request POST 'https://api.planhat.com/companies' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
    "status": "prospect",
    "name": "Tenet",
    "externalId": "Planhat-81ock9l81wl",
    "phase": "Renewal",
    "domains": [
        "abcdinc.com",
        "777.com"
    ],
    "h": 10,
    "csmScore": 4,
    "mr": 0,
    "nrr30": 0,
    "custom": {
        "CompanyTeamMembers": [
            "564b063a508f068051ee82a5",
            "56d5dd4e8422dc141913d0ab"
        ],
        "CompanyTeamMember": "564b063a508f068051ee82a5",
        "CompanyMultiList": [
            "Test2",
            "Test3"
        ],
        "CompanyList": "Test2",
        "CompanyDate": "2020-05-08T21:00:00.000Z",
        "CompanyDay": 18395,
        "CompanyBoolean": true,
        "CompanyNumber": 33,
        "CompanyRating": 4,
        "CompanyRichText": "

sfsdfsdfs

aSADS

SADas

", "CompanyText": "Test" }, "mrrTotal": 0, "nrrTotal": 5555, "mrTotal": 5555, "country": "Latvia", "description": "

Up and coming tech company

" }'

Example Response

200 OK

'{
    "_id": "60ff42f8d627295c259999af",
    "shareable": {
        "team": {
            "fields": []
        },
        "enabled": false,
        "euIds": [],
        "sunits": false
    },
    "followers": [],
    "domains": [
        "abcdinc.com",
        "777.com"
    ],
    "collaborators": [],
    "products": [],
    "tags": [],
    "lastPerformedTriggers": [],
    "status": "prospect",
    "name": "Test Company",
    "externalId": "Planhat-81ock9l81wl",
    "phase": "Renewal",
    "h": 10,
    "csmScore": 4,
    "mr": 0,
    "nrr30": 0,
    "custom": {
        "CompanyRating": 4,
        "CompanyRichText": "

Test

rich

text

", "CompanyText": "Test" }, "mrrTotal": 0, "nrrTotal": 5555, "mrTotal": 5555, "country": "Latvia", "description": "

Up and coming tech company

", "csmScoreLog": [ { "date": "2021-07-26T23:19:20.871Z", "score": 4 } ], "phaseSince": "2021-07-26T23:19:20.873Z", "createDate": "2021-07-26T23:19:20.873Z", "lastUpdated": "2021-07-26T23:19:20.877Z", "lastTouchByType": {}, "sales": [], "licenses": [], "features": {}, "usage": {}, "documents": [], "links": [], "alerts": [], "lastActivities": [], "updatedAt": "2021-07-26T23:19:20.880Z", "__v": 0 }'

Update CompanyPUT

https://api.planhat.com/companies/:_id

To update a company it's required to pass the company _id in the request URL as a parameter.

Alternately it’s possible to update using the company externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/companies/extid-{{externalId}}

or

https://api.planhat.com/companies/srcid-{{sourceId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/companies/60ff42f8d627295c259999af' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "name": "Company new name"
    }'

Example Response

200 OK

'{
    "_id": "60ff42f8d627295c259999af",
    "shareable": {
        "team": {
            "fields": []
        },
        "enabled": false,
        "euIds": [],
        "sunits": false
    },
    "followers": [],
    "domains": [
        "abcdinc.com",
        "777.com"
    ],
    "collaborators": [],
    "products": [],
    "tags": [],
    "lastPerformedTriggers": [],
    "status": "prospect",
    "name": "Company new name",
    "externalId": "Planhat-81ock9l81wl",
    "phase": "Renewal",
    "h": 10,
    "csmScore": 4,
    "mr": 0,
    "nrr30": 0,
    "custom": {
        "CompanyRating": 4,
        "CompanyRichText": "

Test

rich

text

", "CompanyText": "Test", }, "mrrTotal": 0, "nrrTotal": 5555, "mrTotal": 5555, "country": "Latvia", "description": "

Up and coming tech company

", "csmScoreLog": [ { "date": "2021-07-26T23:19:20.871Z", "score": 4 } ], "phaseSince": "2021-07-26T23:19:20.873Z", "createDate": "2021-07-26T23:19:20.873Z", "lastUpdated": "2021-07-26T23:24:47.778Z", "lastTouchByType": {}, "sales": [], "licenses": [], "features": {}, "usage": {}, "documents": [], "links": [], "alerts": [], "updatedAt": "2021-07-26T23:24:47.778Z", "__v": 0 }'

Get Company by IDGET

https://api.planhat.com/companies/:_id

To get a specific company it's required to pass the _id in the request URL as a parameter.

Alternately it's possible get a company using its externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/companies/extid-{{externalId}}

or

https://api.planhat.com/companies/srcid-{{sourceId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/companies/61006bc89a3e0b702ed8ea49' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "61006bc89a3e0b702ed8ea49",
    "shareable": {
        "team": {
            "fields": []
        },
        "enabled": false,
        "euIds": [],
        "sunits": false
    },
    "followers": [],
    "domains": [
        "stjohns.k12.fl.us"
    ],
    "collaborators": [],
    "products": [],
    "tags": [],
    "lastPerformedTriggers": [],
    "owner": "60ccb1c5965cc9e0f3848075",
    "custom": {
        "Days in Phase": 0,
        "Days until renewal": 364,
        "# Overdue Invoices": 1,
    },
    "name": "Tenet",
    "phase": "Onboarding",
    "status": "prospect",
    "phaseSince": "2021-07-27T20:25:44.430Z",
    "sunits": {
        "5b49b52a1ac87812818de26d": {
            "status": "on",
            "default": true
        }
    },
    "createDate": "2021-07-27T20:25:44.430Z",
    "lastUpdated": "2021-08-11T16:12:41.657Z",
    "lastTouchByType": {
        "note": "2021-08-22T16:15:50.772Z"
    },
    "sales": [
        {
            "_id": "61016c80675c1b871faf2d4f",
            "value": 10000,
            "product": "Advanced Onboarding",
            "_currency": {
                "_id": "USD",
                "rate": 1,
                "symbol": "$",
                "isBase": true,
                "__v": 0,
                "overrides": {}
            },
            "salesDate": "2021-07-28T00:00:00.000Z",
            "companyId": "61006bc89a3e0b702ed8ea49",
            "companyName": "Tenet",
            "__v": 0,
            "externalId": "sale-1234"
        }
    ],
    "licenses": [],
    "features": {},
    "usage": {},
    "csmScoreLog": [],
    "documents": [],
    "links": [],
    "alerts": [],
    "lastActivities": [],
    "updatedAt": "2021-08-09T16:54:17.298Z",
    "__v": 0,
    "h": 3,
    "autoRenews": false,
    "customerTo": "2022-07-27T00:00:00.000Z",
    "headId": "6100772b919fd37905810fc6",
    "lastRenewal": "2021-07-27T00:00:00.000Z",
    "mr": 15000,
    "mrTotal": 15000,
    "mrr": 0,
    "mrrTotal": 0,
    "nrr30": 15000,
    "nrrTotal": 15000,
    "renewalDate": "2022-07-27T00:00:00.000Z",
    "renewalDaysFromNow": 363,
    "renewalMrr": 0,
    "nps": 8.5,
    "sourceId": "119",
    "hDiff": -2,
    "hDiffDate": "2021-08-04T20:21:24.117Z",
    "hProfile": "5f87f152f5fc6f26057ac901",
    "orgCount": 0,
    "orgPathCount": 0
}'

Get Companies ListGET

https://api.planhat.com/companies

When fetching multiple companies there are some options that can be used via query params:

  • companyId: Filter using company id. Multiple ids can be used separating them by commas.

  • limit: Limit the list length. Default as 100, max. 5000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/companies?limit=2&offset=0&sort=name' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "61002458cb43626b3a7fb186",
        "shareable": {
            "team": {
                "fields": []
            },
            "enabled": false,
            "euIds": [],
            "sunits": false
        },
        "domains": [
            "https://google.lv",
            "https://draugiem.lv"
        ],
        "tags": [
            "test",
            "test2"
        ],
        "owner": null,
        "coOwner": null,
        "externalId": "23z887cpocs",
        "sourceId": "23z887cpocs",
        "phase": "Onboarding",
        "status": "prospect",
        "h": 3,
        "csmScore": 3,
        "mr": 2,
        "nrr30": 3,
        "mrrTotal": 4,
        "mrTotal": 5,
        "name": "Altus",
        "phaseSince": "2021-07-27T15:20:56.552Z",
        "features": {},
        "usage": {},
        "alerts": [],
        "custom": {
            "Days in Phase": 0,
            "MRR_PLUS_TWOkwu7xl5ziq": 6
        }
    },
    {
        "_id": "6100186428706a6646358453",
        "shareable": {
            "team": {
                "fields": []
            },
            "enabled": false,
            "euIds": [],
            "sunits": false
        },
        "domains": [
            "https://google.lv",
            "https://draugiem.lv"
        ],
        "tags": [
            "test",
            "test2"
        ],
        "owner": null,
        "coOwner": null,
        "externalId": "3mfo0a71pa7",
        "sourceId": "3mfo0a71pa7",
        "phase": "Onboarding",
        "status": "prospect",
        "h": 3,
        "csmScore": 3,
        "mr": 2,
        "nrr30": 3,
        "mrrTotal": 4,
        "mrTotal": 5,
        "name": "Beta",
        "phaseSince": "2021-07-27T14:29:56.119Z",
        "features": {},
        "usage": {},
        "alerts": [],
        "custom": {
            "Days in Phase": 0,
            "MRR_PLUS_TWOkwu7xl5ziq": 6
        }
    }
]'

Get Companies Lean ListGET

https://api.planhat.com/leancompanies

When you need a lightweight list of all companies in Planhat to match against your own ids etc.

For each company profile in Planhat you'll get back the Planhat Id, External Id, Source ID (eg Salesforce) as well as the name.

When fetching lean companies there are some options that can be used via query params:

  • externalId: Compay externalId.

  • sourceId: Company sourceId.

  • status: Company status, e.g. "lost", "prospect".

Example Request

curl --location -g --request GET 'https://api.planhat.com/leancompanies?status=prospect,lost' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "56bccdf554d64d837d01be8c",
        "name": "Chevron",
        "externalId": "chevron",
        "sourceId": "0012000001UchdsAAB"
    },
    {
        "_id": "56ccc2d39b760ff232295798",
        "sourceId": "0012000001Tjy5xAAB",
        "name": "United Oil & Gas, Singapore",
        "externalId": "CD355120-B"
    },
    {
        "_id": "56bccdf554d64d837d01be63",
        "name": "Burger King",
        "externalId": "",
        "sourceId": "0012000001VKEiVAAX"
    },
    {
        "_id": "56bccdf554d64d837d01be4e",
        "name": "McDonalds",
        "externalId": "mcdonalds",
        "sourceId": "0012000001TwtO7AAJ"
    }
]'

Delete CompanyDELETE

https://api.planhat.com/companies/:_id

To delete a company it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/companies/60ff42f8d627295c259999af' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert CompaniesPUT

https://api.planhat.com/companies

To create a company it's required define a name.

To update an company it is required to specify in the payload one of the following keyables, listed in order of priority: _id, sourceId, externalId.

Since this is a bulk upsert operation it's possible create and/or update multiple companies with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/companies' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
        {
            "name": "Skynet",
            "externalId": 1234,
            "custom": {
                "field": "custom field"
            }
        },
        {
            "name": "Tenet",
            "sourceId": "sfc-1235",
            "custom": {
                "field": "custom field"
            }
        }
    ]'

Example Response

200 OK

'{
    "created": 1,
    "createdErrors": [],
    "updatedErrors": [],
    "insertsKeys": [
        {
            "_id": "6100512d9a3e0b702ed8e9c8",
            "sourceId": "sfc-1235"
        }
    ],
    "updated": 1,
    "updatedErrors": [],
    "updatesKeys": [
        {
            "_id": "6011889f52181c5640bf41bb",
            "sourceId": "0012000001U28SYAAZ",
            "externalId": "1234"
        }
    ],
    "nonupdates": 0,
    "modified": [
        "6011889f52181c5640bf41bb"
    ],
    "upsertedIds": [
        "6100512d9a3e0b702ed8e9c8"
    ],
    "permissionErrors": []
}'

Conversation

https://api.planhat.com/conversations

Conversations can be of different types such as email, chat, support tickets and manually logged notes. You can also create your own types in Planhat to represent things such as "in person meeting", "Training" etc. The default types (email, chat, ticket, call) are reserved and should not be created over API.

Property Required Type Description
_id
objectId Planhat identifier.
subject
string Title of the conversation.
companyId
objectId Related company id (planhat identifier).
companyName
string Company name. (Autogenerated).
description
string Description of the conversation.
snippet
string Formatted content of the conversation.
date
string ISO date when conversation was created.
outDate
string ISO date when conversation was sent.
createDate
string ISO date when conversation was created. (Autogenerated).
type
string Type of conversation. Must reference a pre-existent conversation type in Planhat, or it will default to type: note.
users
array Array of user objects.
endusers
array Array of involved contacts (endusers ids). (Autogenerated).
externalId
string The asset id in your own system.
activityTags
array Array of string to tag your notes.
starred
boolean Flag to mark a conversation as important or relevant.
pinned
boolean Pin a conversation at the top of the company and/or user profile.
emailTemplateIds
array Array of email templates ids. (Autogenerated).
isOpen
boolean Set a conversation or ticket as open or not, mostly related to CRM integrations (Autogenerated).
tags
array Array of string to tags for the conversation. (Autogenerated).
waitsToBeFiltered
boolean Internal system field. (Autogenerated).
timeBucket
array Internal system field. (Autogenerated).
archived
boolean Mark a conversation as archived.
numberOfParts
integer Internal system field. (Autogenerated).
sender
array Array of objects the sender information. (Autogenerated).
history
array Internal system field. (Autogenerated).
userIds
array Array of user ids involved in the conversation. (Autogenerated).
hasMore
boolean Internal system field. (Autogenerated).
isCustomType
boolean Mark the conversation as custom type or not. (Autogenerated).
assigneeName
string Internal system field. (Autogenerated).
numberOfRelevantParts
integer Internal system field. (Autogenerated).
hasAttachments
boolean Set to true if the conversation has attachments. (Autogenerated).
isSeen
boolean Set to true to flag the conversation as seen. (Autogenerated).
custom
object Custom object with your custom properties.

Create ConversationPOST

https://api.planhat.com/conversations

To create a conversation, it is required to define a valid companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

Example Request

curl --location -g --request POST 'https://api.planhat.com/conversations' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "users": [
            {
                "id": "58e231b14246fc73139f29e8"
            }
        ],
        "date": "2021-07-29T15:29:34.330Z",
        "type": "note",
        "companyId": "61006bc89a3e0b702ed8ea49",
        "subject": "New conversation",
        "description": "Little call with the client.",
        "endusers": [
            {
                "id": "610091916d643a7c418aef42"
            }
        ],
        "activityTags": [],
        "useSubject": true,
        "ownerId": "58e231b14246fc73139f29e8"
    }'

Example Response

200 OK

'{
    "starred": false,
    "pinned": false,
    "autoTags": [],
    "activityTags": [],
    "emailTemplateIds": [],
    "isOpen": false,
    "tags": [],
    "waitsToBeFiltered": true,
    "timeBucket": [],
    "archived": false,
    "_id": "6102e3b08084189dcbf0e3f0",
    "users": [
        {
            "id": "58e231b14246fc73139f29e8",
            "name": "Alex",
            "isOwner": true
        }
    ],
    "date": "2021-07-29T15:29:34.330Z",
    "type": "note",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "subject": "New conversation",
    "description": "Little call with the client.",
    "endusers": [
        {
            "id": "610091916d643a7c418aef42",
            "name": "Lara Croft"
        }
    ],
    "snippet": "Little call with the client.",
    "companyName": "Tenet",
    "createDate": "2021-07-29T17:21:52.347Z",
    "sender": [],
    "history": [],
    "__v": 0,
    "owner": "60ccb1c5965cc9e0f3848075",
    "followers": [],
    "userId": "610015551b990c65d4fb0a4c",
    "note": "New conversation: Little call with the client.",
    "nickName": "Test user"
}'

Update ConversationPUT

https://api.planhat.com/conversations/:_id

To update an conversation it's required to pass the _id in the request URL as a parameter.

Alternately it’s possible to update using the conversation externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/conversations/extid-{{externalId}}

or

https://api.planhat.com/conversations/srcid-{{sourceId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/conversations/6102e3b08084189dcbf0e3f0' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "subject": "Chat with the client",
        "description": "

Slack chat with the client.

" }'

Example Response

200 OK

'{
    "_id": "6102e3b08084189dcbf0e3f0",
    "starred": false,
    "pinned": false,
    "autoTags": [],
    "activityTags": [],
    "emailTemplateIds": [],
    "isOpen": false,
    "tags": [],
    "waitsToBeFiltered": true,
    "timeBucket": [],
    "archived": false,
    "users": [
        {
            "id": "58e231b14246fc73139f29e8",
            "name": "Alex",
            "isOwner": true
        }
    ],
    "date": "2021-07-29T15:29:34.330Z",
    "type": "note",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "subject": "Chat with the client",
    "description": "Slack chat with the client.",
    "endusers": [
        {
            "id": "610091916d643a7c418aef42",
            "name": "Lara Croft"
        }
    ],
    "snippet": "Slack chat with the client.",
    "companyName": "Tenet",
    "createDate": "2021-07-29T17:21:52.347Z",
    "sender": [],
    "history": [],
    "__v": 0
}'

Get Conversation by IDGET

https://api.planhat.com/conversations/:_id

To get a specific conversation it's required to pass the _id in the request URL as a parameter.

Alternately it's possible get a conversation using its externalId adding a prefix and passing this keyable as identifiers.

Example:

https://api.planhat.com/conversations/extid-{{externalId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/conversations/58e57392c9437d10624593fd' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "58e57392c9437d10624593fd",
    "type": "email",
    "numberOfParts": 1,
    "hasAttachments": false,
    "source": "bcc",
    "subject": "Test 6 on Oct 21 from BCC",
    "snippet": "Two new and one existing endusers!  ",
    "days": 17095,
    "companyId": "56e911619f2ff04215515515",
    "timeBucket": [
        "2016",
        "2016-Q4",
        "2016-10",
        "2016-W43"
    ],
    "history": [],
    "tags": [],
    "isOpen": false,
    "users": [
        {
            "id": "57225af2dfb33aa3022e88e9",
            "name": "Vargha",
            "isOwner": true
        }
    ],
    "endusers": [
        {
            "id": "5b47a07ff1846fd087ede384",
            "name": "Max Payne"
        }
    ],
    "date": "2016-10-21T15:24:05.000Z",
    "__v": 0,
    "companyName": "Bonnier",
    "createDate": "2016-10-21T15:24:05.000Z",
    "outDate": "2016-10-21T15:24:05.000Z",
    "numberOfRelevantParts": 1,
    "custom": {
        "Test FF field to check @today cron job": false,
        "t1": 0,
        "HScore": 0,
        "Days in Phase": 0,
        "NRR30": 0,
        "t123": "undefined",
        "Logins": 0,
        "Renewal in days": -1,
        "Activity Count": 0,
        "Week No": 29.285714285714285
    },
    "userIds": [
        "57225af2dfb33aa3022e88e9"
    ],
    "hasMore": true,
    "isCustomType": false,
    "assigneeName": "",
    "isSeen": false,
    "starred": false,
    "pinned": false,
    "archived": false
}'

Get Conversations ListGET

https://api.planhat.com/conversations

When fetching multiple conversations there are some options that can be used via query params:

  • companyId: Filter using company id. Multiple ids can be used separating them by commas.

  • limit: Limit the list length. Default as 30, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/conversations?limit=2&offset=0' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "date": "2021-07-29T15:29:34.330Z",
        "userIds": [
            "58e231b14246fc73139f29e8"
        ],
        "hasMore": false,
        "isCustomType": false,
        "subject": "Chat with the client",
        "snippet": "Slack chat with the client.",
        "_id": "6102e3b08084189dcbf0e3f0",
        "type": "note",
        "tags": [],
        "createDate": "2021-07-29T17:21:52.347Z",
        "companyId": "61006bc89a3e0b702ed8ea49",
        "companyName": "Leo company",
        "users": [
            {
                "id": "58e231b14246fc73139f29e8",
                "name": "Alex",
                "isOwner": true
            }
        ],
        "endusers": [
            {
                "id": "610091916d643a7c418aef42",
                "name": "Lara Croft"
            }
        ],
        "assigneeName": "",
        "activityTags": [],
        "hasAttachments": false,
        "isSeen": false,
        "starred": false,
        "pinned": false,
        "archived": false
    },
    {
        "date": "2021-07-26T16:51:44.146Z",
        "userIds": [
            "5f16fd8eee876638a9f2483c"
        ],
        "isCustomType": false,
        "subject": "t6",
        "_id": "60fee824a4c764252c877c2b",
        "type": "note",
        "tags": [],
        "createDate": "2021-07-26T16:51:48.720Z",
        "companyId": "584d9d5505ba1b622f04adb3",
        "companyName": "Daimler North America",
        "users": [
            {
                "id": "5f16fd8eee876638a9f2483c",
                "name": "Ernesto",
                "isOwner": true
            }
        ],
        "endusers": [],
        "assigneeName": "",
        "activityTags": [],
        "hasAttachments": false,
        "isSeen": false,
        "starred": false,
        "pinned": false,
        "archived": false,
        "custom": {
            "Activity Count": 0,
            "Days in Phase": 0,
            "Logins": 0
        }
    }
]'

Delete ConversationDELETE

https://api.planhat.com/conversations/:_id

To delete an conversation it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/conversations/6102e3b08084189dcbf0e3f0' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "starred": false,
    "pinned": false,
    "autoTags": [],
    "activityTags": [],
    "emailTemplateIds": [],
    "isOpen": false,
    "tags": [],
    "waitsToBeFiltered": true,
    "timeBucket": [],
    "archived": false,
    "_id": "6102e3b08084189dcbf0e3f0",
    "users": [
        {
            "id": "58e231b14246fc73139f29e8",
            "name": "Alex",
            "isOwner": true
        }
    ],
    "date": "2021-07-29T15:29:34.330Z",
    "type": "note",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "subject": "Chat with the client",
    "description": "

Slack chat with the client.

", "endusers": [ { "id": "610091916d643a7c418aef42", "name": "Lara Croft" } ], "snippet": "Slack chat with the client.", "companyName": "Leo company", "createDate": "2021-07-29T17:21:52.347Z", "sender": [], "history": [], "__v": 0 }'

Bulk Upsert ConversationsPUT

https://api.planhat.com/conversations

To create a conversation it's required define a companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

To update a conversation it is required to specify in the payload the _id or the externalId sourceId.

Since this is a bulk upsert operation it's possible create and/or update multiple projects with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/conversations' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
        {
            "users": [
                {
                    "id": "58e231b14246fc73139f29e8"
                }
            ],
            "date": "2021-07-29T15:29:34.330Z",
            "type": "note",
            "companyId": "61006bc89a3e0b702ed8ea49",
            "subject": "New conversation",
            "description": "Little call with the client.",
            "endusers": [
                {
                    "id": "610091916d643a7c418aef42"
                }
            ],
            "activityTags": [],
            "useSubject": true,
            "ownerId": "58e231b14246fc73139f29e8"
        }
    ]'

Example Response

200 OK

'{
    "created": 1,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "61672167d4ac8780f8f680af"
        }
    ],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [
        "61672167d4ac8780f8f680af"
    ],
    "permissionErrors": []
}'

Custom Field

https://api.planhat.com/customfields

Most objects in Planaht can be customized by creating your own custom fields. Which model a given custom fields belongs is indicated by the parent property.

Typically you would create the custom fields from within the Planhat app. But in some special cases you may find it more convenient to manage over API instead.

Property Required Type Description
_id
objectId Planhat identifier.
name
string Name of the custom field.
type
string Type of the custom field, it should be one of: number, text, rich text, checkbox, day, date, list, multipicklist, team member, team members, rating, phone, email, enduser, endusers, url.
parent
string Parent phModel.
isFeatured
boolean If true the custom field will appear in the parent's detail view, Default as false.
isHidden
boolean If true the custom field will not appear in any parent's view, Default as false.
isShared
boolean If true the custom field will be shareable on portal, Default as false.
isLocked
boolean If true the custom field will not be editable on the UI, Default as false.
isMandatory
boolean If true the custom field will required to create the parent's model, Default as false.
isFormula
boolean True if the custom field is a formula field. Default as false.
treatUndefinedAsZero
boolean If the custom field is a formula field, this will determine if a formula result null/undefined is replaced by zero. This is useful for custom fields type number.
formula
string Formula definition.
listValues
array Array of options for list and multipick custom types.
filter
array Array of parts (objects) of the filter. (Autogenerated).
formulaRefs
array Array of strings of the formula field references. (Autogenerated).
numberFormat
string Custom number format, this is only valid for number types.

Create Custom FieldPOST

https://api.planhat.com/customfields

To create an custom field it's required define a name, parent and a type.

Example Request

curl --location -g --request POST 'https://api.planhat.com/customfields' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "parent": "company",
        "type": "number",
        "isFeatured": true,
        "isHidden": false,
        "name": "Number field",
        "isFormula": false,
        "isLocked": false
    }'

Example Response

200 OK

'{
    "isFeatured": true,
    "isShared": false,
    "listValues": [],
    "filter": [],
    "formulaRefs": [],
    "isFormula": false,
    "_id": "61043ced0b92efb8a6339cd1",
    "parent": "company",
    "type": "number",
    "isHidden": false,
    "name": "Number field",
    "isLocked": false,
    "__v": 0
}'

Update Custom FieldPUT

https://api.planhat.com/customfields/:_id

To update an custom field it's required to pass _id in the request URL as a parameter.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/customfields/61043ced0b92efb8a6339cd1' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "isFeatured": false,
        "isHidden": true
    }'

Example Response

200 OK

'{
    "_id": "61043ced0b92efb8a6339cd1",
    "isFeatured": false,
    "isShared": false,
    "listValues": [],
    "filter": [],
    "formulaRefs": [],
    "isFormula": false,
    "parent": "company",
    "type": "number",
    "isHidden": true,
    "name": "Number field",
    "isLocked": false,
    "__v": 0
}'

Get Custom Field by IDGET

https://api.planhat.com/customfields/:_id

To get a specific custom field it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request GET 'https://api.planhat.com/customfields/5c94de4752e54d5ce538ae18' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "5c94de4752e54d5ce538ae18",
    "isFeatured": true,
    "listValues": [],
    "parent": "invoice",
    "type": "text",
    "isHidden": false,
    "name": "PO Number"
}'

Get Custom Fields ListGET

https://api.planhat.com/customfields

When fetching multiple custom fields there are some options that can be used via query params:

  • limit: Limit the list length. Default as 100, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/customfields?limit=2&offset=0' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "5c8a24fdc923c77bf64dc567",
        "isFeatured": false,
        "listValues": [],
        "parent": "company",
        "type": "number",
        "isHidden": false,
        "name": "Hours Invested",
        "isShared": false,
        "filter": [],
        "sortOrder": 9,
        "isLocked": false
    },
    {
        "_id": "5c94de4752e54d5ce538ae18",
        "isFeatured": true,
        "listValues": [],
        "parent": "invoice",
        "type": "text",
        "isHidden": false,
        "name": "PO Number"
    }
]'

Delete Custom FieldDELETE

https://api.planhat.com/customfields/:_id

To delete an custom field it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/customfields/61043ced0b92efb8a6339cd1' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Endusers

https://api.planhat.com/endusers

An enduser represents an individual at one of your customers, typically a user of your product, a business contact or both. Endusers can automatically be created based on user tracking events, or based on conversations such as emails and tickets.

Often this automatic creation of contacts along with sync from an external CRM or similar is enough. But there are also situations where you may want to be 100% sure all your users exist in Planhat, and then it would make sense to create them in Planhat over api as soon as they get created in your own system.

If companyId is not present in the payload, and the email has a domain already registered within a company, then Planhat will auto-assign the new enduser to the company using domain matching.

Property Required Type Description
_id
objectId Planhat identifier.
email
string Email address of the enduser/contact.
firstName
string Enduser first name.
lastName
string Enduser last name
name
string Composed string using the firstName and lastName. This gets autogenerated if the firstName and/or lastName are defined so there is no need to set it.
companyId
objectId Related company id (planhat identifier).
companyName
string Company name. (Autogenerated).
externalId
string The enduser id in your own system.
sourceId
string The enduser id from an integration (Sales Force, Hubspot, etc).
position
string The role or position of this contact/user. e.g, "CFO".
phone
string Enduser phone number, any string will do.
tags
array Array of strings to tag each contact.
featured
boolean Define if the enduser is a feature contact.
primary
boolean Gives extra weighting when assigning emails.
archived
boolean Set the enduser as achived, if the enduser is archived it wont appear in the listing. Defaults to false.
sharedNotificationsPrefs
object Object containing the notification preferences. (Autogenerated).
otherEmails
array Array of extra emails.
npsUnsubscribed
boolean Flag to show if the enduser has unsubcribed to NPS.
nps
integer The last NPS score submitted by the enduser. (Autogenerated).
npsComment
string The comment that was submitted with the NPS score. (Autogenerated).
npsDate
string The date when the last NPS score was received for this enduser. (Autogenerated).
npsSent
string The date when the last NPS survey was sent to this enduser. (Autogenerated).
beatTrend
integer Internal system field. (Autogenerated).
beats
integer Beats count. (Autogenerated).
beatsTotal
integer Total count of beats. (Autogenerated).
convs14
integer Internal system field. (Autogenerated).
convsTotal
integer Internal system field. (Autogenerated).
experience
integer Internal system field. (Autogenerated).
createDate
string ISO date when enduser was created. (Autogenerated).
updatedAt
string ISO date when enduser was updated. (Autogenerated).
lastActivities
array Array of objects containing the last activities. (Autogenerated).
relatedEndusers
array Array of objects containing the related or duplicate endusers. (Autogenerated).
lastTouch
string ISO date when enduser was last time contacted. (Autogenerated).
lastTouchType
string Type of last contact. (Autogenerated).
lastTouchByType
object Object containing the history of last contact dates by type. (Autogenerated).
custom
object A flexible object with custom data.

Create EnduserPOST

https://api.planhat.com/endusers

To create an enduser it's required define in the payload a companyId and an email, externalId or sourceId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

Example Request

curl --location -g --request POST 'https://api.planhat.com/endusers' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
                "featured": true,
                "email": "jane@test.com",
                "firstName": "Jane",
                "lastName": "Doe",
                "companyId": "61006bc89a3e0b702ed8ea49"
            }'

Example Response

200 OK

'{
    "sharedNotificationsPrefs": {
        "enabled": [],
        "disabled": [],
        "disabledEvents": []
    },
    "otherEmails": [],
    "featured": true,
    "tags": [],
    "npsUnsubscribed": false,
    "beatTrend": 0,
    "beats": 0,
    "convs14": 0,
    "convsTotal": 0,
    "beatsTotal": 0,
    "experience": 0,
    "_id": "610091916d643a7c418aef42",
    "email": "jane@test.com",
    "firstName": "Jane",
    "lastName": "Doe",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "companyName": "Tenet",
    "name": "Jane Doe",
    "createDate": "2021-07-27T23:06:57.083Z",
    "lastActivities": [],
    "relatedEndusers": [],
    "updatedAt": "2021-07-27T23:06:57.086Z",
    "emailMd5": "550cf97cea5f2ce1f7f6a5b06c1c51a8",
    "__v": 0
}'

Update EnduserPUT

https://api.planhat.com/endusers/:_id

To update an enduser it's required to pass the _id in the request URL as a parameter.

Alternately it’s possible to update using the enduser externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/endusers/extid-{{externalId}}

or

https://api.planhat.com/endusers/srcid-{{sourceId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/endusers/610091916d643a7c418aef42' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "firstName": "Lara",
         "lastName": "Croft"
    }'

Example Response

200 OK

'{
    "_id": "610091916d643a7c418aef42",
    "sharedNotificationsPrefs": {
        "enabled": [],
        "disabled": [],
        "disabledEvents": []
    },
    "otherEmails": [],
    "featured": true,
    "tags": [],
    "npsUnsubscribed": false,
    "beatTrend": 0,
    "beats": 0,
    "convs14": 0,
    "convsTotal": 0,
    "beatsTotal": 0,
    "experience": 0,
    "email": "jane@test.com",
    "firstName": "Lara",
    "lastName": "Croft",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "companyName": "Tenet",
    "name": "Lara Croft",
    "createDate": "2021-07-27T23:06:57.083Z",
    "relatedEndusers": [],
    "updatedAt": "2021-07-27T23:09:02.889Z",
    "emailMd5": "550cf97cea5f2ce1f7f6a5b06c1c51a8",
    "__v": 0
}'

Get Enduser by IDGET

https://api.planhat.com/endusers/:_id

To get a specific enduser it's required to pass the _id in the request URL as a parameter.

Alternately it's possible get an enduser using its externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/endusers/extid-{{externalId}}

or

https://api.planhat.com/endusers/srcid-{{sourceId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/endusers/56bccdf554d64d837d01bf42' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "56bccdf554d64d837d01bf42",
    "companyId": "56bccdf554d64d837d01be4a",
    "companyName": "Google",
    "name": "Sadie Santos",
    "email": "sadie.santos_5@google.com",
    "lastActive": "2021-03-03T00:00:00.000Z",
    "beats": 0,
    "beatsTotal": 286.01014298852533,
    "beatTrend": 0,
    "experience": 49,
    "firstName": "Sadie",
    "lastName": "Santos",
    "relevanceRate": 0,
    "relevance": 86.82,
    "convsTotal": 9,
    "convs14": 1,
    "tags": [
        "Main User"
    ],
    "lastTouch": "2021-07-08T04:00:00.000Z",
    "lastTouchType": "QBR",
    "lastTouchByType": {
        "note": "2019-07-22T18:22:08.774Z",
        "Onboarding": "2019-11-11T19:14:16.902Z",
        "QBR": "2021-07-08T04:00:00.000Z"
    },
    "custom": {
        "Days in Phase": 0,
        "Logins": 0,
        "Renewal in days": -1,
        "Roles": "role2",
        "Activity Count": 0,
        "Week No": 29.285714285714285
    },
    "sourceId": "0030O00002m9M9UQAU",
    "createDate": "2016-02-11T18:07:49.000Z",
    "emailMd5": "86dcc89d519bb31ea34e4033b0919cab",
    "featured": false,
    "npsUnsubscribed": false,
    "otherEmails": [],
    "relatedEndusers": [],
    "sharedNotificationsPrefs": {
        "disabled": [],
        "disabledEvents": [],
        "enabled": []
    },
    "__v": 1,
    "updatedAt": "2021-07-12T09:49:56.192Z",
    "position": "CSM",
    "description": "",
    "npsSent": "2021-05-18T16:10:03.468Z"
}'

Get Endusers ListGET

https://api.planhat.com/endusers

When fetching multiple endusers there are some options that can be used via query params:

  • companyId: Filter using company id. Multiple ids can be used separating them by commas.

  • limit: Limit the list length. Default as 100, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

  • archived: Passing true the endpoint will include archived endusers.

  • email: Passing a valid email the endpoint will return a list of endusers with that email.

  • euId: Passing a valid Planhat ID (_id) the endpoint will return the enduser with that id.

Example Request

curl --location -g --request GET 'https://api.planhat.com/endusers?limit=2&offset=0' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "56bccdf554d64d837d01bf2c",
        "companyId": "56bccdf554d64d837d01be59",
        "companyName": "t2",
        "name": "John Bakers",
        "email": "antonella.silva_1@att.com",
        "lastActive": "2021-07-27T02:50:04.000Z",
        "beats": 60,
        "beatsTotal": 2957.390884043649,
        "beatTrend": -3.2,
        "experience": 88,
        "firstName": "John",
        "lastName": "Bakers",
        "relevanceRate": 0,
        "relevance": 97.11,
        "convsTotal": 1,
        "convs14": 0,
        "tags": [
            "Main Contact",
            "mahamadou",
            "userss",
            "New Tag"
        ],
        "lastTouch": "2020-05-05T11:34:48.437Z",
        "lastTouchType": "Technical Life-lesson",
        "lastTouchByType": {
            "Technical Life-lesson": "2020-05-05T11:34:48.437Z"
        },
        "custom": {
            "UserType": "Admin",
            "Customer Role": "",
            "Days in Phase": 0,
            "Logins": 0,
            "Renewal in days": -1,
            "Roles": "role2",
            "Activity Count": 0,
            "Week No": 29.285714285714285
        },
        "sourceId": "0030O00002jbaN1QAI",
        "position": "Fireman",
        "phone": "07606356442",
        "featured": true,
        "createDate": "2016-02-11T18:07:49.000Z",
        "updatedAt": "2021-07-12T09:49:56.321Z",
        "emailMd5": "ea31df0c7a36b8c4c2fcddbe5faa6f4c",
        "npsUnsubscribed": false,
        "otherEmails": [],
        "sharedNotificationsPrefs": {
            "disabled": [],
            "disabledEvents": [],
            "enabled": []
        },
        "__v": 1,
        "companyExternalId": "ATT",
        "lastUpdated": "2020-10-07T16:38:47.014Z",
        "description": "",
        "externalId": "Anton",
        "npsSent": "2021-05-18T16:10:03.468Z"
    },
    {
        "_id": "56bccdf554d64d837d01bf42",
        "companyId": "56bccdf554d64d837d01be4a",
        "companyName": "Google",
        "name": "Sadie Santos",
        "email": "sadie.santos_5@google.com",
        "lastActive": "2021-03-03T00:00:00.000Z",
        "beats": 0,
        "beatsTotal": 286.01014298852533,
        "beatTrend": 0,
        "experience": 49,
        "firstName": "Sadie",
        "lastName": "Santos",
        "relevanceRate": 0,
        "relevance": 86.82,
        "convsTotal": 9,
        "convs14": 1,
        "tags": [
            "Main User"
        ],
        "lastTouch": "2021-07-08T04:00:00.000Z",
        "lastTouchType": "QBR",
        "lastTouchByType": {
            "note": "2019-07-22T18:22:08.774Z",
            "Onboarding": "2019-11-11T19:14:16.902Z",
            "QBR": "2021-07-08T04:00:00.000Z"
        },
        "custom": {
            "Days in Phase": 0,
            "Logins": 0,
            "Renewal in days": -1,
            "Roles": "role2",
            "Activity Count": 0,
            "Week No": 29.285714285714285
        },
        "sourceId": "0030O00002m9M9UQAU",
        "createDate": "2016-02-11T18:07:49.000Z",
        "emailMd5": "86dcc89d519bb31ea34e4033b0919cab",
        "featured": false,
        "npsUnsubscribed": false,
        "otherEmails": [],
        "relatedEndusers": [],
        "sharedNotificationsPrefs": {
            "disabled": [],
            "disabledEvents": [],
            "enabled": []
        },
        "__v": 1,
        "updatedAt": "2021-07-12T09:49:56.192Z",
        "position": "CSM",
        "description": "",
        "npsSent": "2021-05-18T16:10:03.468Z"
    }
]'

Delete EnduserDELETE

https://api.planhat.com/endusers/:_id

To delete an enduser it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/endusers/610091916d643a7c418aef42' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert EndusersPUT

https://api.planhat.com/endusers

To create an enduser it's required define a companyId, and one of: email, externalId, sourceId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

To update an enduser it is required to specify in the payload one of the following keyables, listed in order of priority: _id, sourceId, externalId, email.

Since this is a bulk upsert operation it's possible create and/or update multiple endusers with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/endusers' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
                {
                    "email": "jane@test.com",
                    "lastName": "Smith"
                },
                {
                    "featured": true,
                    "email": "will@test.com",
                    "firstName": "Will",
                    "lastName": "Smith",
                    "companyId": "61006bc89a3e0b702ed8ea49"
                }
            ]'

Example Response

200 OK

'{
    "created": 1,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "610096a46d643a7c418af043",
            "email": "will@test.com"
        }
    ],
    "updated": 0,
    "updatedErrors": [
        {
            "item": {
                "email": "jane@test.com",
                "lastName": "Smith",
                "_id": "610091916d643a7c418aef42",
                "name": "Lara Smith"
            },
            "err": [
                {
                    "error": "Not valid type"
                }
            ]
        }
    ],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [
        "610096a46d643a7c418af043"
    ],
    "permissionErrors": []
}'

Invoice

https://api.planhat.com/invoices

Invoices are normally generated automatically in Planhat when a license is created or renewed, invoices can include multiple line items. Planhat will not prepare invoices that you actually can send to your customers though. They're rather meant to help anyone working with your customers to know the status of current and past invoicing.

Invoices default date fields format should be days format integer. (Days since January 1, 1970, Unix epoch)

Property Required Type Description
_id
objectId Planhat identifier.
currency
string The currency code, for example "USD". There is a "soft" mapping to the id’s in your list of currencies. Technically we accept any string but we recommend using standard currency codes as Ids. Since the mapping is soft you can add sales with a currency code not yet available in your list of currencies.
product
string Name of the sales product. Not required but highly recommended. Even if you only have one product we suggest adding it as "Advanced Onboarding".
cId
objectId Related company id (planhat identifier).
cName
string Company name. (Autogenerated).
extId
string The invoice id in your own system.
sourceId
string The invoice id from an integration (Salesforce, Hubspot, etc).
invoiceDate
integer Date when invoice was created should be in days format integer.
lastUpdated
string ISO date when invoice was updated. (Autogenerated).
invoiceNo
integer Invoice number. (Autogenerated).
dueDate
integer Date when invoice expires in days format integer.
paidDate
integer Date when invoice is paid should be in days format integer.
amountTotal
number Invoice amount.
status
string Status of invoice could be pending, paid, overdue.
lineItems
array Array of license objects. (Autogenerated).
emails
array Internal system field. (Autogenerated).
addressCountry
string Country where the invoice is issued. (Autogenerated).
invoiceCycle
string Cycle of the invoice. Could be one of: monthly, quarterly, yearly, semiAnnual, once or custom. (Autogenerated).
custom
object A flexible object with custom data.

Create InvoicePOST

https://api.planhat.com/invoices

To create an invoice the only real value that is required is the cId but it doesn't make much sense to have an invoice just with a cId, that is why we suggest specify a invoiceDate and currency.

You can instead reference the company externalId or sourceId using the following command structure: "cId": "extid-[company externalId]" or "cId": "srcid-[company sourceId]".

Example Request

curl --location -g --request POST 'https://api.planhat.com/invoices' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
                "dueDate": 19201,
                "invoiceDate": 18836,
                "currency": "USD",
                "amountTotal": 10000,
                "lineItems": [
                    {
                        "relatedId": "sale-61016c80675c1b871faf2d4f",
                        "saleId": "61016c80675c1b871faf2d4f",
                        "product": "Advanced Onboarding",
                        "amount": 10000,
                        "dateFrom": 18836
                    }
                ],
                "cId": "61006bc89a3e0b702ed8ea49"
            }'

Example Response

200 OK

'{
    "invoiceNo": 1,
    "amountTotal": 10000,
    "emails": [],
    "status": "pending",
    "_id": "61017a0372c0e0884d5e827e",
    "dueDate": 19201,
    "invoiceDate": 18836,
    "currency": "USD",
    "lineItems": [
        {
            "_id": "61017a0372c0e0884d5e827f",
            "saleId": "61016c80675c1b871faf2d4f",
            "product": "Advanced Onboarding",
            "amount": 10000,
            "dateFrom": 18836
        }
    ],
    "cId": "61006bc89a3e0b702ed8ea49",
    "custom": {
        "Sent Date": "2021-07-02T04:00:00.000Z",
        "Sent": true
    },
    "cName": "Tenet",
    "updatedAt": "2021-07-28T15:38:43.243Z",
    "__v": 0
}'

Update InvoicePUT

https://api.planhat.com/invoices/:_id

To update a invoice it's required to pass the invoice _id in the request URL as a parameter.

Alternately it’s possible to update using the invoice extId (externalId) and/or sourceId adding a prefix and passing this keyable as identifier.

Example:

https://api.planhat.com/invoices/extid-{{extId}}

or

https://api.planhat.com/invoices/srcid-{{sourceId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/invoices/61017a0372c0e0884d5e827e' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "paidDate": 18840
    }'

Example Response

200 OK

'{
    "_id": "61017a0372c0e0884d5e827e",
    "invoiceNo": 1,
    "amountTotal": 10000,
    "emails": [],
    "status": "pending",
    "dueDate": 19201,
    "invoiceDate": 18836,
    "currency": "USD",
    "lineItems": [
        {
            "_id": "61017a0372c0e0884d5e827f",
            "saleId": "61016c80675c1b871faf2d4f",
            "product": "Advanced Onboarding",
            "amount": 10000,
            "dateFrom": 18836
        }
    ],
    "cId": "61006bc89a3e0b702ed8ea49",
    "custom": {
        "Sent Date": "2021-07-02T04:00:00.000Z",
        "Sent": true
    },
    "cName": "Tenet",
    "updatedAt": "2021-07-28T15:56:16.385Z",
    "__v": 0,
    "paidDate": 18840
}'

Get Invoice by IDGET

https://api.planhat.com/invoices/:_id

To get a specific invoice it's required to pass the _id in the request URL as a parameter.

Alternately it's possible get a invoice using its extId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/invoices/extid-{{extId}}

or

https://api.planhat.com/invoices/srcid-{{sourceId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/invoices/5bec1521b63ce46d84dd7a7d' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "5bec1521b63ce46d84dd7a7d",
    "invoiceNo": 1,
    "amountDue": 226029,
    "amountPaid": 226029,
    "emails": [],
    "status": "paid",
    "cId": "598b753cc488358437483eb9",
    "cName": "Alphabet Inc.",
    "invoiceDate": 17752,
    "dueDate": 18116,
    "lineItems": [
        {
            "_id": "5bec1521b63ce46d84dd7a7e",
            "licenseId": "5baa914c6919c0ec85106069",
            "product": "License Fee",
            "amount": 244864.75,
            "dateFrom": 17752,
            "dateTo": 18116
        }
    ],
    "__v": 0,
    "paidDate": 18673,
    "invoiceCycle": "once",
    "custom": {
        "t1": 0,
        "HScore": 0,
        "Days in Phase": 0,
        "Sent": true,
        "NRR30": 0,
        "t123": "undefined",
        "Logins": 0,
        "Renewal in days": -1,
        "Activity Count": 0,
        "Week No": 29.285714285714285
    },
    "updatedAt": "2021-02-15T15:36:41.372Z",
    "comments": [],
    "amountTotal": 244864.75,
    "lastUpdated": "2021-05-28T11:35:12.265Z"
}'

Get Invoices ListGET

https://api.planhat.com/invoices

When fetching multiple invoices there are some options that can be used via query params:

  • companyId: Filter using company id. Multiple ids can be used separating them by commas.

  • limit: Limit the list length. Default as 100, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/invoices?limit=2&offset=0&sort=-amountTotal&select=_id,product,currency,cId,amountTotal' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "5bec1521b63ce46d84dd7a7d",
        "invoiceNo": 1,
        "amountDue": 226029,
        "amountPaid": 226029,
        "emails": [],
        "status": "paid",
        "cId": "598b753cc488358437483eb9",
        "cName": "Alphabet Inc.",
        "invoiceDate": 17752,
        "dueDate": 18116,
        "lineItems": [
            {
                "_id": "5bec1521b63ce46d84dd7a7e",
                "licenseId": "5baa914c6919c0ec85106069",
                "product": "License Fee",
                "amount": 244864.75,
                "dateFrom": 17752,
                "dateTo": 18116
            }
        ],
        "__v": 0,
        "paidDate": 18673,
        "invoiceCycle": "once",
        "custom": {
            "t1": 0,
            "HScore": 0,
            "Days in Phase": 0,
            "Sent": true,
            "NRR30": 0,
            "t123": "undefined",
            "Logins": 0,
            "Renewal in days": -1,
            "Activity Count": 0,
            "Week No": 29.285714285714285
        },
        "updatedAt": "2021-02-15T15:36:41.372Z",
        "comments": [],
        "amountTotal": 244864.75,
        "lastUpdated": "2021-05-28T11:35:12.265Z"
    },
    {
        "_id": "5bec958059e144aa751dd7e7",
        "invoiceNo": 1,
        "amountDue": 150890,
        "amountPaid": 0,
        "emails": [],
        "status": "paid",
        "dueDate": 17880,
        "invoiceDate": 17850,
        "currency": "USD",
        "lineItems": [
            {
                "_id": "5bec958059e144aa751dd7e8",
                "licenseId": "598b765d49c568a81b052acc",
                "product": "License Fee",
                "amount": 150890,
                "dateFrom": 17386,
                "dateTo": 17746
            }
        ],
        "cId": "598b762249c568a81b052a75",
        "cName": "Amazon",
        "__v": 0,
        "custom": {
            "t1": 0,
            "HScore": 0,
            "Days in Phase": 0,
            "Sent": true,
            "NRR30": 0,
            "t123": "undefined",
            "Logins": 0,
            "Renewal in days": -1,
            "Activity Count": 0,
            "Week No": 29.285714285714285
        },
        "updatedAt": "2020-10-01T13:46:32.781Z",
        "amountTotal": 150890,
        "paidDate": 18551,
        "lastUpdated": "2021-05-28T11:35:12.265Z"
    }
]'

Delete InvoiceDELETE

https://api.planhat.com/invoices/:_id

To delete a invoice it's required to pass the _id in the request URL as a parameter.

Invoice numbers are immutable. That means that if you have five invoices, 1, 2, 3, 4, 5, and delete the third, you will end with 1, 2, 4, 5 and the next one will be number 6.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/invoices/61017a0372c0e0884d5e827e' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert InvoicePUT

https://api.planhat.com/invoices

To create an invoice it's required define a cId, currency and invoiceDate.

You can instead reference the company externalId or sourceId using the following command structure: "cId": "extid-[company externalId]" or "cId": "srcid-[company sourceId]".

To update an invoice it is required to specify in the payload one of the following keyables, listed in order of priority: _id, sourceId, extId.

Since this is a bulk upsert operation it's possible create and/or update multiple invoices with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/invoices' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
                {
                    "dueDate": 19201,
                    "invoiceDate": 18834,
                    "currency": "USD",
                    "amountTotal": 10000,
                    "lineItems": [
                        {
                            "relatedId": "sale-61016c80675c1b871faf2d4f",
                            "saleId": "61016c80675c1b871faf2d4f",
                            "product": "Advanced Onboarding",
                            "amount": 10000,
                            "dateFrom": 18836
                        }
                    ],
                    "cId": "61006bc89a3e0b702ed8ea49"
                },
                    {
                    "dueDate": 19210,
                    "invoiceDate": 18832,
                    "currency": "USD",
                    "amountTotal": 15000,
                    "lineItems": [
                        {
                            "relatedId": "sale-61016c80675c1b871faf2d4f",
                            "saleId": "61016c80675c1b871faf2d4f",
                            "product": "Advanced Onboarding",
                            "amount": 10000,
                            "dateFrom": 18836
                        }
                    ],
                    "cId": "61006bc89a3e0b702ed8ea49"
                }
            ]'

Example Response

200 OK

'{
    "created": 2,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "6101954772c0e0884d5e8b4f"
        },
        {
            "_id": "6101954772c0e0884d5e8b50"
        }
    ],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [
        "6101954772c0e0884d5e8b4f",
        "6101954772c0e0884d5e8b50"
    ],
    "permissionErrors": []
}'

Issue

https://api.planhat.com/issues

Issues typically represent Bugs or Feature Requests. Many of our customers fetch issues from Jira, but they can also be pushed to Planhat from other product management tools such as Product Board or Aha! You can also manage issues directly in Planhat without any external tool. Just keep in mind that the functionality is basic and mostly intended to contribute to the customer 360 view.

Issues in Planhat can link to multiple companies, to multiple endusers and to multiple conversations.

Property Required Type Description
_id
objectId Planhat identifier.
title
string Issue title, required to create an issue.
description
string Description for the issue.
companyIds
array Array of companies ids involved with the issue.
companies
array Array of companies involved with the issue. (Autogenerated).
enduserIds
array Array of endusers ids that are part of the issue.
endusers
array Array of endusers that are part of the issue. (Autogenerated).
conversationIds
array Array of conversations ids that are part of the issue.
sourceId
string The issue id from an integration (Sales Force, Hubspot, etc).
source
string Source name or description.
sourceUrl
string Source URL.
priority
string Priority name or description.
archived
boolean True is the issued is archived.
status
string Current status of the issue.
mrrCombined
integer Total MRR involved in this issue. (Autogenerated).
arrCombined
integer Total ARR involved in this issue. (Autogenerated).
createdAt
string ISO date when issue was created. (Autogenerated).
updatedAt
string ISO date when issue was updated. (Autogenerated).
followers
array Array of users ids following the issue.
custom
object A flexible object with custom data.

Create IssuePOST

https://api.planhat.com/issues

To create a issue the only real value that is required is the title but it doesn't make much sense to have an issue just with a title, that is why we suggest specify a companyIds.

You can instead reference the company externalId or sourceId using the following command structure: "companyIds": ["extid-[company externalId]"] or "companyIds": ["srcid-[company sourceId]"].

Example Request

curl --location -g --request POST 'https://api.planhat.com/issues' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "companyIds": [
            "61006bc89a3e0b702ed8ea49"
        ],
        "enduserIds": [
            "610091916d643a7c418aef42"
        ],
        "title": "Problem with integration",
        "description": "There is a problem with our integration settings that is affecting some users.",
        "status": "Open"
    }'

Example Response

200 OK

'{
    "companyMatchingValue": [],
    "companyIds": [
        "61006bc89a3e0b702ed8ea49"
    ],
    "enduserIds": [
        "610091916d643a7c418aef42"
    ],
    "conversationIds": [],
    "archived": false,
    "_id": "610411d5b046afb2109df12c",
    "title": "Problem with integration",
    "description": "There is a problem with our integration settings that is affecting some users.",
    "status": "Open",
    "companies": [
        {
            "id": "61006bc89a3e0b702ed8ea49",
            "name": "Tenet"
        }
    ],
    "endusers": [
        {
            "id": "610091916d643a7c418aef42",
            "name": "Lara Croft"
        }
    ],
    "updatedAt": "2021-07-30T14:51:01.691Z",
    "createdAt": "2021-07-30T14:51:01.691Z",
    "__v": 0
}'

Update IssuePUT

https://api.planhat.com/issues/:_id

To update a issue it's required to pass the issue _id in the request URL as a parameter.

Alternately it’s possible to update using the issue sourceId adding a prefix and passing this keyable as identifier.

Example:

https://api.planhat.com/issues/srcid-{{sourceId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/issues/610411d5b046afb2109df12c' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "status": "In Progress"
    }'

Example Response

200 OK

'{
    "_id": "61041245b046afb2109df1bb",
    "companyMatchingValue": [],
    "companyIds": [
        "61006bc89a3e0b702ed8ea49"
    ],
    "enduserIds": [],
    "conversationIds": [],
    "archived": false,
    "title": "Problem with integration",
    "description": "There is a problem with our integration settings that is affecting some users.",
    "status": "In Progress",
    "companies": [
        {
            "id": "61006bc89a3e0b702ed8ea49",
            "name": "Tenet"
        }
    ],
    "endusers": [],
    "updatedAt": "2021-07-30T14:52:53.441Z",
    "createdAt": "2021-07-30T14:52:53.441Z",
    "__v": 0
}'

Get Issue by IDGET

https://api.planhat.com/issues/:_id

To get a specific issue it's required to pass the _id in the request URL as a parameter.

Alternately it's possible get a issue using its sourceId adding a prefix and passing this keyable as identifier.

Example:

https://api.planhat.com/issues/srcid-{{sourceId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/issues/60f7df113432fd36abf8b899' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "60f7df113432fd36abf8b899",
    "companyMatchingValue": [],
    "companyIds": [
        "56bccdf554d64d837d01be9d"
    ],
    "enduserIds": [],
    "conversationIds": [],
    "archived": false,
    "title": "There is a problem with the sites",
    "companies": [],
    "endusers": [],
    "updatedAt": "2021-07-21T08:47:13.221Z",
    "createdAt": "2021-07-21T08:47:13.221Z",
    "__v": 0,
    "status": "Done"
}'

Get Issues ListGET

https://api.planhat.com/issues

When fetching multiple issues there are some options that can be used via query params:

  • companyId: Filter using company id.

  • limit: Limit the list length. Default as 100, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/issues?limit=2&offset=0&sort=-createdAt&select=_id,title,status,companyIds' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "61041245b046afb2109df1bb",
        "companyIds": [
            "61006bc89a3e0b702ed8ea49"
        ],
        "title": "Problem with integration",
        "status": "In Progress"
    },
    {
        "_id": "61002501cb43626b3a802f52",
        "companyIds": [
            60f7df113432fd36abf8b899
        ],
        "title": "New issue",
        "status": "Done"
    }
]'

Delete IssueDELETE

https://api.planhat.com/issues/:_id

To delete an issue it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/issues/610411d5b046afb2109df12c' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert IssuePUT

https://api.planhat.com/issues

To create an issue it's required define a title and companyIds.

You can instead reference the company externalId or sourceId using the following command structure: "companyIds": ["extid-[company externalId]"] or "companyIds": ["srcid-[company sourceId]"].

To update an issue it is required to specify in the payload one of the following keyables, listed in order of priority: _id, sourceId.

Since this is a bulk upsert operation it's possible create and/or update multiple issues with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/issues' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
        {
            "_id": "60f7df113432fd36abf8b899",
            "status": "Done"
        },
        {
            "companyIds": [
                "61006bc89a3e0b702ed8ea49"
            ],
            "enduserIds": [
                "610091916d643a7c418aef42"
            ],
            "title": "Problem with integration",
            "description": "There is a problem with our integration settings that is affecting some users.",
            "status": "Open"
        }
    ]'

Example Response

200 OK

'{
    "created": 1,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "610415ecb046afb2109df26b"
        }
    ],
    "updated": 1,
    "updatedErrors": [],
    "updatesKeys": [
        {
            "_id": "60f7df113432fd36abf8b899"
        }
    ],
    "nonupdates": 0,
    "modified": [
        "60f7df113432fd36abf8b899"
    ],
    "upsertedIds": [
        "610415ecb046afb2109df26b"
    ],
    "permissionErrors": []
}'

Licenses

https://api.planhat.com/licenses

Licenses represent your customers' subcriptions to your service and is the base for MRR (or ARR) calculations and most revenue reports. For non recurring revenue, please see the Sale (NRR) object. There are many ways to get license data into Planhat including incomming webhooks and CRM integrations. In some case though, you just want to handle it yourself over the api, for example if the main source of license data is your own system.

Licenses in Planhat can be fixed period with a defined start and end date. Or they can be non fixed period (sometimes called open-ended or evergreen). Open ended licenses initially don't have a specified end date since the customer may cancel at any time.. once the license is churned/lost also non fixed period licenses can have an end date.

Property Required Type Description
_id
objectId Planhat identifier.
product
string Name of the license product. Not required but highly recommended. Even if you only have one product we suggest adding it as "License Fee" or "Subscription".
_currency
string The currency code, for example "USD". There is a "soft" mapping to the id’s in your list of currencies. Technically we accept any string but we recommend using standard currency codes as Ids. Since the mapping is soft you can add licenses with a currency code not yet available in your list fo currencies.
companyId
objectId Related company id (planhat identifier).
companyName
string Company name. (Autogenerated).
externalId
string The license id in your own system.
sourceId
string The license id from an integration (Sales Force, Hubspot, etc).
fromDate
string Start of the license period (ISO format).
toDate
string End of the license period (ISO format).
fixedPeriod
boolean Boolean to indicate if this is an ongoing license (no defined end date) or license with a fixed term. Non fixed-term licenses require an mrr value specified, whereas fixed-term licenses specify the value over the whole period. Defaults to false.
mrr
number Monthly license value (required if fixedPeriod is false).
arr
number Annual license value.
renewalMrr
number Renewal MRR.
renewalArr
number Renewal ARR.
fcNewMrr
number Monthly value forecast.
fcNewArr
number Annual value forecast.
fcNewMrrOptimistic
number Optimistic monthly value forecast.
fcNewArrOptimistic
number Optimistic annual value forecast.
fcNewMrrPessimistic
number Pessimistic monthly value forecast.
fcNewArrPessimistic
number Pessimistic annual value forecast.
value
number The total license value for the whole subscription period, must be positive (required if fixedPeriod is true and no mrr value).
renewalStatus
string Can take one of the following values: 'ongoing', 'renewed', 'lost'.
renewalPeriod
integer Defines the length of the next license period if the subscription should be renewed. Defaults to 1, which would mean the license renews with a new 1-month period, regardless of the length of the current license period. If the renewalPeriod is not set, it will be assumed that the contract renews on the same period as previous. If the license isn’t set to autorenew the renewalPeriod will only serve as guidance for the manual input.
renewalDate
string ISO date when license will be renewed. (Autogenerated).
renewalDaysFromNow
integer Number of days till the next renewal. (Autogenerated).
isOverdue
boolean Flag the license as overdue. (Autogenerated).
autoRenews
boolean If set to true Planhat can automatically renew the subscription for you once the notice period is passed. Necessary for noticePeriod and noticeUnit to be autogenerated. Defaults to false.
noticePeriod
integer Number of days/weeks/months before subscription ends that the license would have to be canceled not to automatically renew. Only makes sense if autoRenews is set to true. Defaults to 0. (Autogenerated).
noticeUnit
string Unit for the noticePeriod, could be days/weeks/months. (Autogenerated).
renewalUnit
string Unit for the noticePeriod, could be day/week/month. Defaults to month.
parent
string Planhat ID of the ultimate parent license.
invoicesGenerated
boolean Set to true for the invoices to not be autogenerated for the license upon creation. Default: false.
invoiceMuted
boolean Set to true to disable the invoices creation when license renewal. Default: false.
invoiceCycleInterval
integer Defines the custom cycle interval. (Autogenerated).
invoiceCycle
string Cycle of the invoice. Could be one of: monthly, quarterly, yearly, semiAnnual, once or custom. (Autogenerated).
toDateIncluded
boolean Set to true if the license has toDate (Autogenerated).
length
number Internal system field. (Autogenerated).
custom
object A flexible object with custom data.

Create LicensePOST

https://api.planhat.com/licenses

To create a lincese the only real value that is required is the companyId but it doesn't make much sense to have a license just with a companyId, that is why we suggest specify a value, _currency and fromDate.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

Example Request

curl --location -g --request POST 'https://api.planhat.com/licenses' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
                "autoRenews": false,
                "companyId": "61006bc89a3e0b702ed8ea49",
                "fixedPeriod": true,
                "fromDate": "2021-07-27T00:00:00.000Z",
                "toDate": "2022-07-27T00:00:00.000Z",
                "mrr": 83.33333333333333,
                "product": "Large License",
                "_currency": "USD",
                "renewalPeriod": 12,
                "renewalStatus": "ongoing"
            }'

Example Response

200 OK

'{
    "renewalStatus": "ongoing",
    "issues": [],
    "autoRenews": false,
    "noticePeriod": null,
    "renewalUnit": "month",
    "noticeUnit": null,
    "invoicesGenerated": false,
    "_id": "61006fbf1df7fa77a20d9d0a",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "fixedPeriod": true,
    "fromDate": "2021-07-27T00:00:00.000Z",
    "toDate": "2022-07-27T00:00:00.000Z",
    "invoiceCycleInterval": 0,
    "mrr": 83.33333333333333,
    "product": "Large License",
    "_currency": "USD",
    "renewalPeriod": 12,
    "companyName": "Tenet",
    "toDateIncluded": false,
    "length": 12,
    "value": 1000,
    "renewalDate": "2022-07-27T00:00:00.000Z",
    "renewalDaysFromNow": 364,
    "isOverdue": false,
    "__v": 0
}'

Update LicensePUT

https://api.planhat.com/licenses/:_id

To update a license it's required to pass the license _id in the request URL as a parameter.

Alternately it’s possible to update using the license externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/licenses/extid-{{externalId}}

or

https://api.planhat.com/licenses/srcid-{{sourceId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/licenses/61006fbf1df7fa77a20d9d0a' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "mrr": 100000
    }'

Example Response

200 OK

'{
    "_id": "61006fbf1df7fa77a20d9d0a",
    "renewalStatus": "ongoing",
    "issues": [],
    "autoRenews": false,
    "noticePeriod": null,
    "renewalUnit": "month",
    "noticeUnit": null,
    "invoicesGenerated": false,
    "companyId": "61006bc89a3e0b702ed8ea49",
    "fixedPeriod": true,
    "fromDate": "2021-07-27T00:00:00.000Z",
    "toDate": "2022-07-27T00:00:00.000Z",
    "invoiceCycleInterval": 0,
    "mrr": 100000,
    "product": "Large License",
    "_currency": "USD",
    "renewalPeriod": 12,
    "companyName": "Tenet",
    "toDateIncluded": false,
    "length": 12,
    "value": 1200000,
    "renewalDate": "2022-07-27T00:00:00.000Z",
    "renewalDaysFromNow": 364,
    "isOverdue": false,
    "__v": 0
}'

Get License by IDGET

https://api.planhat.com/licenses/:_id

To get a specific license it's required to pass the _id in the request URL as a parameter.

Alternately it's possible get a license using its externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/licenses/extid-{{externalId}}

or

https://api.planhat.com/licenses/srcid-{{sourceId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/licenses/56bccdf554d64d837d01beb2' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "56bccdf554d64d837d01beb2",
    "renewalStatus": "renewed",
    "issues": null,
    "autoRenews": false,
    "noticePeriod": null,
    "renewalUnit": "month",
    "noticeUnit": null,
    "invoicesGenerated": false,
    "isOverdue": false,
    "renewalDaysFromNow": -1313,
    "renewalDate": "2017-12-22T00:00:00.000Z",
    "mrr": 1958.3333333333333,
    "length": 24,
    "fixedPeriod": true,
    "product": "",
    "value": 47000,
    "_currency": "USD",
    "fromDate": "2015-12-22T00:00:00.000Z",
    "toDate": "2017-12-22T00:00:00.000Z",
    "salesDate": "2015-12-22T18:07:49.000Z",
    "renewalPeriod": 24,
    "toDateIncluded": false,
    "invoiceCycleInterval": 0,
    "sourceId": "0060O00000xdyr4QAA",
    "companyId": "56bccdf554d64d837d01beaf",
    "companyName": "Siemens",
    "custom": {
        "Days in Phase": 0,
        "NRR30": 0,
        "t123": "undefined",
        "Logins": 0,
        "Days until renewal": -1,
        "Renewal in days": -1,
        "Activity Count": 0,
        "Week No": 29.285714285714285
    }
}'

Get Licenses ListGET

https://api.planhat.com/licenses

When fetching multiple licenses there are some options that can be used via query params:

  • companyId: Filter using company id.

  • limit: Limit the list length. Default as 100, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/licenses?limit=2&offset=0&sort=-value&select=_id,product,value,companyId' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "601188a152181c5640bf429f",
        "companyId": "6011889f52181c5640bf41bb",
        "value": 292500
    },
    {
        "_id": "598b7911c488358437484797",
        "value": 280950,
        "product": "",
        "companyId": "598b78ef49c568a81b052f49"
    }
]'

Delete LicenseDELETE

https://api.planhat.com/licenses/:_id

To delete a license it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/licenses/61006fbf1df7fa77a20d9d0a' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert LicensesPUT

https://api.planhat.com/licenses

To create a license it's required define a companyId, _currency and value.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

To update a license it is required to specify in the payload one of the following keyables, listed in order of priority: _id, sourceId, externalId.

Since this is a bulk upsert operation it's possible create and/or update multiple licenses with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/licenses' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
                {
                    "_id": "6100772b919fd37905810fc6",
                    "mrr": 200000
                },
                {
                    "autoRenews": false,
                    "companyId": "61006bc89a3e0b702ed8ea49",
                    "fixedPeriod": true,
                    "fromDate": "2021-07-28T00:00:00.000Z",
                    "toDate": "2022-07-28T00:00:00.000Z",
                    "invoiceCycleInterval": 0,
                    "mrr": 100,
                    "product": "Large License",
                    "_currency": "USD",
                    "renewalPeriod": 12,
                    "renewalStatus": "ongoing"
                }
            ]'

Example Response

200 OK

'{
    "created": 1,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "61007752919fd37905810ff5"
        }
    ],
    "updated": 1,
    "updatedErrors": [],
    "updatesKeys": [
        {
            "_id": "6100772b919fd37905810fc6"
        }
    ],
    "nonupdates": 0,
    "modified": [
        "6100772b919fd37905810fc6"
    ],
    "upsertedIds": [
        "61007752919fd37905810ff5"
    ],
    "permissionErrors": []
}'

Notes

https://api.planhat.com/conversations

Notes in Planhat are technically Conversations. You can create your own custom Touch Types to easily distinguish between different types of notes. You can also use custom fields to add more nuance to your Notes.

It's quite common for Notes in Planhat to sync with external systems such as Salesforce, Notes can also be created via Zapier or Planhats's native incoming webhooks.

Property Required Type Description
_id
objectId Planhat identifier.
companyId
objectId Related company id (planhat identifier).
subject
string Title of the note.
description
string Description of the note.
date
string Date when note was created. In ISO format.
activityTags
array Array of tag's objectId.
users
array Array of user's objects.
companyExternalId
string The External Company Id.
type
string For this model should be note.
endusers
array Array of enduser's objects.
custom
object A flexible object with custom data.

Create NotePOST

https://api.planhat.com/conversations

To create an note it's required define in the payload a companyId and type as note.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

Example Request

curl --location -g --request POST 'https://api.planhat.com/conversations' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
                "users": [
                    {
                        "id": "5d66266e187f60020bc8036f",
                        "name": "Ivars",
                        "isOwner": true
                    }
                ],
                "date": "2021-08-22T16:15:50.772Z",
                "type": "note",
                "companyId": "61006bc89a3e0b702ed8ea49",
                "subject": "Support with Tenet",
                "description": "Support session with the client",
                "activityTags": [
                    "5f514794dc005f275e9cc20c"
                ],
                "endusers": [
                    {
                        "id": "610091916d643a7c418aef42",
                        "name": "Jane Doe"
                    }
                ]
            }'

Example Response

200 OK

'{
    "starred": false,
    "pinned": false,
    "autoTags": [],
    "activityTags": [
        "5f514794dc005f275e9cc20c"
    ],
    "emailTemplateIds": [],
    "isOpen": false,
    "tags": [],
    "waitsToBeFiltered": true,
    "timeBucket": [],
    "archived": false,
    "_id": "61081aede03cf31e13ea1e51",
    "users": [
        {
            "id": "5d66266e187f60020bc8036f",
            "name": "Ivars",
            "isOwner": true
        }
    ],
    "date": "2021-08-22T16:15:50.772Z",
    "type": "note",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "subject": "Support with Tenet",
    "description": "Support session with the client",
    "endusers": [
        {
            "id": "610091916d643a7c418aef42",
            "name": "Jane Doe"
        }
    ],
    "snippet": "Support session with the client",
    "companyName": "Tenet",
    "createDate": "2021-08-02T16:18:53.730Z",
    "sender": [],
    "history": [],
    "__v": 0,
    "companySourceId": "119",
    "owner": "60ccb1c5965cc9e0f3848075",
    "followers": [],
    "userId": "610015551b990c65d4fb0a4c",
    "note": "Support with Tenet: Support session with the client",
    "nickName": "Jest tests"
}'

Update NotePUT

https://api.planhat.com/conversations/:_id

To update an notes it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/conversations/61081aede03cf31e13ea1e51' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "subject": "Possible upgrade",
        "description": "

The client is insterested in a subscription upgrade.

" }'

Example Response

200 OK

'{
    "_id": "61081aede03cf31e13ea1e51",
    "starred": false,
    "pinned": false,
    "autoTags": [],
    "activityTags": [
        "5f514794dc005f275e9cc20c"
    ],
    "emailTemplateIds": [],
    "isOpen": false,
    "tags": [],
    "waitsToBeFiltered": true,
    "timeBucket": [],
    "archived": false,
    "users": [
        {
            "id": "5d66266e187f60020bc8036f",
            "name": "Ivars",
            "isOwner": true
        }
    ],
    "date": "2021-08-22T16:15:50.772Z",
    "type": "note",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "subject": "Possible upgrade",
    "description": "

The client is insterested in a subscription upgrade.

", "endusers": [ { "id": "610091916d643a7c418aef42", "name": "Jane Doe" } ], "snippet": "The client is insterested in a subscription upgrade.", "companyName": "Tenet", "createDate": "2021-08-02T16:18:53.730Z", "sender": [], "history": [], "__v": 0 }'

Get Note by IDGET

https://api.planhat.com/conversations/:_id

To get a specific note it's required to pass the _id in the request URL as a parameter.

Alternately it's possible get a note using its externalId adding a prefix and passing this keyable as identifiers.

Example:

https://api.planhat.com/conversations/extid-{{externalId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/conversations/60fee824a4c764252c877c2b' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "date": "2021-07-26T16:51:44.146Z",
    "userIds": [
        "5f16fd8eee876638a9f2483c"
    ],
    "isCustomType": false,
    "subject": "t6",
    "_id": "60fee824a4c764252c877c2b",
    "type": "note",
    "tags": [],
    "createDate": "2021-07-26T16:51:48.720Z",
    "companyId": "584d9d5505ba1b622f04adb3",
    "companyName": "Daimler North America",
    "users": [
        {
            "id": "5f16fd8eee876638a9f2483c",
            "name": "Ernesto",
            "isOwner": true
        }
    ],
    "endusers": [],
    "assigneeName": "",
    "activityTags": [],
    "hasAttachments": false,
    "isSeen": false,
    "starred": false,
    "pinned": false,
    "archived": false,
    "custom": {
        "Activity Count": 0,
        "Days in Phase": 0,
        "Logins": 0
    }
}'

Get Notes ListGET

https://api.planhat.com/conversations

When fetching multiple notes there are some options that can be used via query params:

  • companyId: Filter using company id. Multiple ids can be used separating them by commas.

  • limit: Limit the list length. Default as 30, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/conversations?limit=2&offset=0' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "date": "2021-07-29T15:29:34.330Z",
        "userIds": [
            "58e231b14246fc73139f29e8"
        ],
        "hasMore": false,
        "isCustomType": false,
        "subject": "Chat with the client",
        "snippet": "Slack chat with the client.",
        "_id": "6102e3b08084189dcbf0e3f0",
        "type": "note",
        "tags": [],
        "createDate": "2021-07-29T17:21:52.347Z",
        "companyId": "61006bc89a3e0b702ed8ea49",
        "companyName": "Leo company",
        "users": [
            {
                "id": "58e231b14246fc73139f29e8",
                "name": "Alex",
                "isOwner": true
            }
        ],
        "endusers": [
            {
                "id": "610091916d643a7c418aef42",
                "name": "Lara Croft"
            }
        ],
        "assigneeName": "",
        "activityTags": [],
        "hasAttachments": false,
        "isSeen": false,
        "starred": false,
        "pinned": false,
        "archived": false
    },
    {
        "date": "2021-07-26T16:51:44.146Z",
        "userIds": [
            "5f16fd8eee876638a9f2483c"
        ],
        "isCustomType": false,
        "subject": "t6",
        "_id": "60fee824a4c764252c877c2b",
        "type": "note",
        "tags": [],
        "createDate": "2021-07-26T16:51:48.720Z",
        "companyId": "584d9d5505ba1b622f04adb3",
        "companyName": "Daimler North America",
        "users": [
            {
                "id": "5f16fd8eee876638a9f2483c",
                "name": "Ernesto",
                "isOwner": true
            }
        ],
        "endusers": [],
        "assigneeName": "",
        "activityTags": [],
        "hasAttachments": false,
        "isSeen": false,
        "starred": false,
        "pinned": false,
        "archived": false,
        "custom": {
            "Activity Count": 0,
            "Days in Phase": 0,
            "Logins": 0
        }
    }
]'

Delete NoteDELETE

https://api.planhat.com/conversations/:_id

To delete an note it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/conversations/61081aede03cf31e13ea1e51' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "starred": false,
    "pinned": false,
    "autoTags": [],
    "activityTags": [
        "5f514794dc005f275e9cc20c"
    ],
    "emailTemplateIds": [],
    "isOpen": false,
    "tags": [],
    "waitsToBeFiltered": true,
    "timeBucket": [],
    "archived": false,
    "_id": "61081aede03cf31e13ea1e51",
    "users": [
        {
            "id": "5d66266e187f60020bc8036f",
            "name": "Ivars",
            "isOwner": true
        }
    ],
    "date": "2021-08-22T16:15:50.772Z",
    "type": "note",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "subject": "Possible upgrade",
    "description": "

The client is insterested in a subscription upgrade.

", "endusers": [ { "id": "610091916d643a7c418aef42", "name": "Jane Doe" } ], "snippet": "The client is insterested in a subscription upgrade.", "companyName": "Tenet", "createDate": "2021-08-02T16:18:53.730Z", "sender": [], "history": [], "__v": 0 }'

Bulk Upsert NotesPUT

https://api.planhat.com/conversations

To create a note it's required define a companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

To update a note it is required to specify in the payload the _id.

Since this is a bulk upsert operation it's possible create and/or update multiple projects with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/conversations' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
        {
            "users": [
                {
                    "id": "5d66266e187f60020bc8036f",
                    "name": "Ivars",
                    "isOwner": true
                }
            ],
            "date": "2021-08-22T16:15:50.772Z",
            "type": "note",
            "companyId": "{{companyId}}",
            "subject": "Support with Tenet",
            "description": "Support session with the client",
            "activityTags": [
                "5f514794dc005f275e9cc20c"
            ],
            "endusers": [
                {
                    "id": "610091916d643a7c418aef42",
                    "name": "Jane Doe"
                }
            ]
        }
    ]'

Example Response

200 OK

'{
    "created": 1,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "61672167d4ac8780f8f680af"
        }
    ],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [
        "61672167d4ac8780f8f680af"
    ],
    "permissionErrors": []
}'

NPS

https://api.planhat.com/nps

NPS records in Planhat represent the individual responses to an nps survey. Typically these are created automatically when running an nps campaign in Planhat, or in some cases imported from external NPS tools. A single enduser/contact can have multiple records if they responded to different surveys over time.

Based on the NPS records each enduser and company in Planhat also get an nps score assigned.

Property Required Type Description
_id
objectId Planhat identifier.
cId
objectId Related company id (planhat identifier).
email
string Email of the contact that has been surveyed.
euId
objectId Enduser id. (Autogenerated).
campaignId
objectId ID of the campaign associated with the NPS.
score
integer Feedback score that the user submit in his/her survey (0-10).
comment
string The comment when the user gives an score in survey.
sourceId
string The project id from an integration (Sales Force, Hubspot, etc).
dateSent
string ISO date when the survey was sent.
dateAnswered
string ISO date when the survey was answered.
source
string The name of the system that NPS scores come from. For example: "custom-nps.com".
custom
object Custom object with your custom properties.

Create NPSPOST

https://api.planhat.com/nps

To create an NPS it's required define an enduser email and a score.

Example Request

curl --location -g --request POST 'https://api.planhat.com/nps' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "email": "jane@test.com",
        "score": 7
    }'

Example Response

200 OK

'{
    "_id": "61030b0e8084189dcbf0e6da",
    "email": "jane@test.com",
    "score": 7,
    "euId": "610091916d643a7c418aef42",
    "cId": "61006bc89a3e0b702ed8ea49",
    "scoreType": "passive",
    "dateSent": null,
    "dateAnswered": "2021-07-29T20:09:50.914Z",
    "__v": 0
}'

Update NPSPUT

https://api.planhat.com/nps/:_id

To update an NPS it's required to pass the NPS _id in the request URL as a parameter.

Alternately it’s possible to update using the NPS sourceId adding a prefix.

Example:

https://api.planhat.com/nps/srcid-{{sourceId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/nps/61030b0e8084189dcbf0e6da' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "score": 9,
        "comment": "Good product"
    }'

Example Response

200 OK

'{
    "_id": "61030b0e8084189dcbf0e6da",
    "campaignId": "60915415fc985e73734f5c02",
    "email": "jane@test.com",
    "score": 9,
    "euId": "610091916d643a7c418aef42",
    "cId": "61006bc89a3e0b702ed8ea49",
    "scoreType": "promoter",
    "dateSent": null,
    "dateAnswered": "2021-07-29T20:09:50.914Z",
    "__v": 0,
    "comment": "Good product"
}'

Get NPS by IDGET

https://api.planhat.com/nps/:_id

To get a specific NPS it's required to pass the _id in the request URL as a parameter.

Alternately it's possible get a NPS using its sourceId adding a prefix.

Example:

https://api.planhat.com/nps/srcid-{{sourceId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/nps/5a2692f13a3f1f4202cf281f' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "5a2692f13a3f1f4202cf281f",
    "npsId": "5f3bc3efe8cd350bb6cf715a",
    "email": "taco@trello.com",
    "npsDate": "2020-09-14T06:20:59.367Z",
    "nps": 6,
    "npsComment": null,
    "npsSent": null,
    "scoreType": "detractor",
    "cId": "598b76adc488358437484229",
    "campaignId": "5c3d0c38b95614b78f100c65",
    "dateSent": "2020-08-18T12:10:03.495Z",
    "dateReminderSent": null,
    "source": "Planhat",
    "sourceId": "5f3bc3efe8cd350bb6cf715a",
    "custom": null
}'

Get NPS ListGET

https://api.planhat.com/nps

When fetching multiple NPS there are some options that can be used via query params:

  • companyId: Filter using company id. Multiple ids can be used separating them by commas.

  • limit: Limit the list length. Default as 100, max. 10000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

  • npsExpiryDay: Integer representing the number of days before the curret date where the

    NPS was answered. The default value is 360, meaning that be default you will get NPS answered from 1 year to the current date.

Example Request

curl --location -g --request GET 'https://api.planhat.com/nps?limit=2&offset=0' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "58a161c86ffa1e5706ca4e81",
        "npsId": "5f0f2312d8c9ff05273778c5",
        "email": "david.lundstrom@sweco.se",
        "npsDate": "2020-08-06T00:00:00.000Z",
        "nps": 8,
        "npsComment": "Good one",
        "npsSent": null,
        "scoreType": "passive",
        "cId": "56bccdf554d64d837d01be55",
        "campaignId": null,
        "dateSent": "2020-06-06T00:00:00.000Z",
        "dateReminderSent": null,
        "source": null,
        "sourceId": "",
        "custom": null
    },
    {
        "_id": "5a2692f13a3f1f4202cf281f",
        "npsId": "5f3bc3efe8cd350bb6cf715a",
        "email": "taco@trello.com",
        "npsDate": "2020-09-14T06:20:59.367Z",
        "nps": 6,
        "npsComment": null,
        "npsSent": null,
        "scoreType": "detractor",
        "cId": "598b76adc488358437484229",
        "campaignId": "5c3d0c38b95614b78f100c65",
        "dateSent": "2020-08-18T12:10:03.495Z",
        "dateReminderSent": null,
        "source": "Planhat",
        "sourceId": "5f3bc3efe8cd350bb6cf715a",
        "custom": null
    }
]'

Delete NPSDELETE

https://api.planhat.com/nps/:_id

To delete an NPS it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/nps/61030b0e8084189dcbf0e6da' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert NPSPUT

https://api.planhat.com/nps

To create a NPS it's required define an enduser email and a score.

To update a NPS it is required to specify in the payload one of the following keyables, listed in order of priority: _id, sourceId.

Since this is a bulk upsert operation it's possible create and/or update multiple NPS with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/nps' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
        {
            "email": "jane@test.com",
            "score": 7
        },
            {
            "email": "pepe@test.com",
            "score": 10
        }
    ]'

Example Response

200 OK

'{
    "created": 2,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "610313dee41b0aa81c926b69"
        },
        {
            "_id": "610313dee41b0aa81c926b6a"
        }
    ],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [
        "610313dee41b0aa81c926b69",
        "610313dee41b0aa81c926b6a"
    ],
    "permissionErrors": []
}'

Opportunity

https://api.planhat.com/opportunities

Opportunities in Planhat represent a sales opportunity, whether it's selling to a new customer or more commonly a chance of expanding an existing account.

Opportunities are not the sames as Licenses, but when an opportunity is closed won in Planhat, there is an optional setting to generate a licenses based on the opportunity data.

Property Required Type Description
_id
objectId Planhat identifier.
salesStage
string Sales stage of opportunity.
companyId
objectId Related company id (planhat identifier).
companyName
string Company name. (Autogenerated).
dealDate
string Deal date in ISO format.
landingDate
string Landing date of the opportunity in ISO format.
closeDate
string Close date of the opportunity in ISO format.
externalId
string The project id in your own system.
sourceId
string The opportunity id from an integration (Salesforce, Hubspot, etc).
_currency
string The currency code, for example "USD".
title
string Title of sales opportunity.
ownerId
objectId ObjectId of the team member owner of the opportunity.
mrr
number Monthly opportunity value.
arr
number Annual opportunity value.
nrr
number Non recurrent value.
contractLength
integer Contract Length (months, defaults to 12).
status
string Status of the opportunity, could be: active, won or lost.
stageHistory
array Array of object containing the history of this opportunity. (Autogenerated).
custom
object Custom object with your custom properties.

Create OpportunityPOST

https://api.planhat.com/opportunities

To create an opportunity it's required define a companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

Example Request

curl --location -g --request POST 'https://api.planhat.com/opportunities' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
    "salesStage": "Pitched",
    "dealDate": "2021-08-03T00:00:00.000Z",
    "ownerId": "60ccb1c5965cc9e0f3848075",
    "_currency": "USD",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "title": "New opportunity",
    "recurringValue": 3000,
    "mrr": 250,
    "nrr": 500,
    "status": "active"
}'

Example Response

200 OK

'{
    "status": "active",
    "_id": "6101e99b72c0e0884d5e9139",
    "salesStage": "Pitched",
    "dealDate": "2021-08-03T00:00:00.000Z",
    "ownerId": "60ccb1c5965cc9e0f3848075",
    "_currency": "USD",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "title": "New opportunity",
    "mrr": 250,
    "nrr": 500,
    "companyName": "Tenet",
    "stageHistory": [],
    "landingDate": "2021-07-28T23:34:51.636Z",
    "__v": 0
}'

Update OpportunityPUT

https://api.planhat.com/opportunities/:_id

To update an opportunity it's required to pass the opportunity _id in the request URL as a parameter.

Alternately it’s possible to update using the opportunity externalId adding a prefix and passing this keyable as identifier.

Example:

https://api.planhat.com/opportunities/extid-{{externalId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/opportunities/6101e99b72c0e0884d5e9139' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "status": "won",
        "salesStage": "Closed won"
    }'

Example Response

200 OK

'{
    "_id": "6101e99b72c0e0884d5e9139",
    "status": "won",
    "salesStage": "Closed won",
    "dealDate": "2021-08-03T00:00:00.000Z",
    "ownerId": "60ccb1c5965cc9e0f3848075",
    "_currency": "USD",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "title": "New opportunity",
    "mrr": 250,
    "nrr": 500,
    "companyName": "Leo company",
    "stageHistory": [],
    "landingDate": "2021-07-28T23:34:51.636Z",
    "__v": 0,
    "closeDate": "2021-07-28T23:36:59.294Z"
}'

Get Opportunity by IDGET

https://api.planhat.com/opportunities/:_id

To get a specific opportunity it's required to pass the _id in the request URL as a parameter.

Alternately it's possible get an opportunity using its externalId and/or sourceId adding a prefix and passing one of these keyable as identifier.

Example:

https://api.planhat.com/opportunities/extid-{{externalId}}

or

https://api.planhat.com/opportunities/srcid-{{sourceId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/opportunities/58499569e2914f5171fa7f9d' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "58499569e2914f5171fa7f9d",
    "status": "lost",
    "companyId": "56bccdf554d64d837d01be86",
    "stageHistory": [
        {
            "stage": "opportunity",
            "to": "2016-12-08T17:16:25.153Z"
        }
    ],
    "landingDate": "2018-11-29T11:57:06.444Z",
    "comments": [],
    "__v": 0,
    "custom": null,
    "companyName": "Toyota"
}'

Get Opportunity ListGET

https://api.planhat.com/opportunities

When fetching multiple opportunities there are some options that can be used via query params:

  • companyId: Filter using company id.

  • limit: Limit the list length. Default as 100, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/opportunities?limit=2&offset=0&sort=-mrr&select=_id,title,status' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "5ced0734861084738e27ff21",
        "status": "lost",
        "title": "P1 enabled!"
    },
    {
        "_id": "5804f2e90abc84bf13ac5cd7",
        "title": "Up sell Opportunity",
        "status": "won"
    }
]'

Delete OpportunityDELETE

https://api.planhat.com/opportunities/:_id

To delete an opportunity it's required to pass the opportunity _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/opportunities/6101e99b72c0e0884d5e9139' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert OpportunitiesPUT

https://api.planhat.com/opportunities

To create a opportunity it's required define a valid companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

To update a opportunity it is required to specify in the payload one of the following keyables, listed in order of priority: _id, externalId.

Since this is a bulk upsert operation it's possible create and/or update multiple projects with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/opportunities' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
        {
            "salesStage": "Pitched",
            "dealDate": "2021-08-10T00:00:00.000Z",
            "ownerId": "60ccb1c5965cc9e0f3848075",
            "_currency": "USD",
            "companyId": "61006bc89a3e0b702ed8ea49",
            "title": "New license opportunity",
            "recurringValue": 6000,
            "status": "active"
        },
        {
            "salesStage": "Pitched",
            "dealDate": "2021-08-03T00:00:00.000Z",
            "ownerId": "60ccb1c5965cc9e0f3848075",
            "_currency": "USD",
            "companyId": "61006bc89a3e0b702ed8ea49",
            "title": "New sale opportunity",
            "recurringValue": 1000,
            "status": "active"
        }
    ]'

Example Response

200 OK

'{
    "created": 2,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "6101ebd072c0e0884d5e91fb"
        },
        {
            "_id": "6101ebd072c0e0884d5e91fc"
        }
    ],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [
        "6101ebd072c0e0884d5e91fb",
        "6101ebd072c0e0884d5e91fc"
    ],
    "permissionErrors": []
}'

Objective

https://api.planhat.com/objectives

Being very clear and focused on your goals with customers is critical, and now you can track objectives and the health per objective.

Pro-tip: use your average Objective health in the Health Score!

Property Required Type Description
_id
objectId Planhat identifier.
name
string Objective name.
health
number Objective health.
companyId
objectId Related company id (planhat identifier).
companyName
string Company name. (Autogenerated).
externalId
string The objective id in your own system.
sourceId
string The objective id from an integration (Sales Force, Hubspot, etc).
custom
object Custom object with your custom properties.

Create ObjectivePOST

https://api.planhat.com/objectives

To create an objective it's required define a name and a valid companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

Example Request

curl --location -g --request POST 'https://api.planhat.com/objectives' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
    "name": "Objective name",
    "health": 5,
    "companyId": "60df26bfa259250cba9cf816",
    "externalId": 1234,
    "sourceId": "sfc-1234",
    "custom": {
        "field": "custom field"
    }
}'

Example Response

200 OK

'{
    "_id": "60faeda853b8f717ebe36146",
    "name": "Objective name",
    "health": 5,
    "companyId": "60df26bfa259250cba9cf816",
    "externalId": "1234",
    "sourceId": "sfc-1234",
    "custom": {
        "field": "custom field"
    },
    "companyName": "Test Company"
}'

Update ObjectivePUT

https://api.planhat.com/objectives/:_id

To update an objective it's required to pass the _id in the request URL as a parameter.

Alternately it’s possible to update using the objective externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/objectives/extid-{{externalId}}

or

https://api.planhat.com/objectives/srcid-{{sourceId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/objectives/60faeda853b8f717ebe36146' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "name": "Objective name 2"
    }'

Example Response

200 OK

'{
    "_id": "60ff061d681a6b4da9248694",
    "name": "Objective name 2",
    "health": 5,
    "companyId": "60df26bfa259250cba9cf816",
    "externalId": "12323",
    "companyName": "Test Company"
    "__v": 0,
    "sourceId": "12323"
}'

Get Objectives by IDGET

https://api.planhat.com/objectives/:_id

To get a specific objective it's required to pass the _id in the request URL as a parameter.

Alternately it's possible to get an objective using its externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/objectives/extid-{{externalId}}

or

https://api.planhat.com/objectives/srcid-{{sourceId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/objectives/5ffcce42ad67267f66741147' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "5ffcce42ad67267f66741147",
    "companyId": "56bccdf554d64d837d01be9d",
    "companyName": "Tenet",
    "name": "Gold objective",
    "health": 5,
    "__v": 0,
    "custom": {
        "Comments": "",
        "Main Product": [
            "Recipe Database"
        ],
        "Days in Phase": 0,
        "NRR30": 0,
        "t123": "undefined",
        "Logins": 0,
        "Overall Outlet Health": 4,
        "Kary FF": null
    },
    "externalId": "4467",
    "usage": {
        "6053b728fa96fa0262729a3d": 0,
        "6053ba62ca3eaf023a54d268": 0
    }
}'

Get Objectives ListGET

https://api.planhat.com/objectives

When fetching multiple objectives there are some options that can be used via query params:

  • companyId: Filter using company id.

  • limit: Limit the list length. Default as 100, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/objectives?limit=2&offset=0&sort=-name&select=_id,name,health,companyId' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "60fb0869694ea374023924cb",
        "companyId": "56bccdf554d64d837d01be4a",
        "name": "ZPA Product",
        "health": 5
    },
    {
        "_id": "601c0a253e5ed41388982528",
        "companyId": "6011889f52181c5640bf41ba",
        "name": "Trello.io",
        "health": 4
    }
]'

Delete ObjectiveDELETE

https://api.planhat.com/objectives/:_id

To delete an objective it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/objectives/60faeda853b8f717ebe36146' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert ObjectivesPUT

https://api.planhat.com/objectives

To create an objective it's required define a name and a valid companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

To update an objective it is required to specify in the payload one of the following keyables, listed in order of priority: _id, sourceId, externalId.

Since this is a bulk upsert operation it's possible create and/or update multiple objectives with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/objectives' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
        {
            "name": "Objective name",
            "companyId": "60df26bfa259250cba9cf816",
            "health": 4,
            "externalId": 1234,
            "sourceId": "sfc-1234",
            "custom": {
                "field": "custom field"
            }
        },

        {
            "name": "Objective name 2",
            "companyId": "60df26bfa259250cba9cf816",
            "health": 5,
            "externalId": 1235,
            "sourceId": "sfc-1235",
            "custom": {
                "field": "custom field"
            }
        }
    ]'

Example Response

200 OK

'{
    "created": 2,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "60fde9ad57df5b411cf39be5",
            "sourceId": "sfc-1234",
            "externalId": "1234"
        },
        {
            "_id": "60fde9ad57df5b411cf39be6",
            "sourceId": "sfc-1235",
            "externalId": "1235"
        }
    ],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [
        "60fde9ad57df5b411cf39be5",
        "60fde9ad57df5b411cf39be6"
    ],
    "permissionErrors": []
}'

Project

https://api.planhat.com/projects

Projects can represent many different real world objects with a natural start and stop date. A service provider for schools may use Projects to represent classes or courses. If you're selling a software to run sales competitions, then each competition may be a project.

Using custom fields you can tailor projects to your needs, and just like Assets, usage data and time series data (metrics) can be associated with your Projetcs.

Property Required Type Description
_id
objectId Planhat identifier.
name
string Project name.
companyId
objectId Related company id (planhat identifier).
companyName
string Company name. (Autogenerated).
externalId
string The project id in your own system.
sourceId
string The project id from an integration (Sales Force, Hubspot, etc).
currency
string The currency code, for example "USD".
startDate
string The project start date in ISO format.
endDate
string The project end date in ISO format.
mrr
number Monthly project value.
arr
number Annual project value.
nrr
number Non recurrent project value.
custom
object Custom object with your custom properties.

Create ProjectPOST

https://api.planhat.com/projects

To create an project it's required define a name and a valid companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

Example Request

curl --location -g --request POST 'https://api.planhat.com/projects' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
    "companyId": "61006bc89a3e0b702ed8ea49",
    "startDate": "2021-07-26T04:00:00.000Z",
    "endDate": "2021-07-31T04:00:00.000Z",
    "currency": "USD",
    "mrr": 1000,
    "name": "New project"
}'

Example Response

200 OK

'{
    "_id": "6101a50072c0e0884d5e8db1",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "startDate": "2021-07-26T04:00:00.000Z",
    "endDate": "2021-07-31T04:00:00.000Z",
    "currency": "USD",
    "mrr": 1000,
    "name": "New project",
    "companyName": "Tenet",
    "__v": 0
}'

Update ProjectPUT

https://api.planhat.com/projects/:_id

To update an project it's required to pass the project _id in the request URL as a parameter.

Alternately it’s possible to update using the project externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/projects/extid-{{externalId}}

or

https://api.planhat.com/projects/srcid-{{sourceId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/projects/6101a50072c0e0884d5e8db1' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "name": "Big project"
    }'

Example Response

200 OK

'{
    "_id": "6101a50072c0e0884d5e8db1",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "startDate": "2021-07-26T04:00:00.000Z",
    "endDate": "2021-07-31T04:00:00.000Z",
    "currency": "USD",
    "mrr": 1000,
    "name": "Big project",
    "companyName": "Tenet",
    "__v": 0,
    "custom": {
        "Days until project end": 2
    }
}'

Get Project by IDGET

https://api.planhat.com/projects/:_id

To get a specific project it's required to pass the _id in the request URL as a parameter.

Alternately it's possible get a project using its externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/projects/extid-{{externalId}}

or

https://api.planhat.com/projects/srcid-{{sourceId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/projects/609a88660b73992504816060' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "609a88660b73992504816060",
    "companyId": "609975100b73992504810554",
    "companyName": "Tenet",
    "name": "SAP Integration",
    "__v": 0,
    "startDate": "2021-05-12T03:00:00.000Z",
    "custom": {
        "Notes": "",
        "Product": [
            "Product A"
        ],
        "Days in Phase": 0,
        "NRR30": 0,
        "t123": "undefined",
        "Logins": 0,
        "Renewal in days": -1,
        "Activity Count": 0,
        "Week No": 29.285714285714285
    },
    "mrr": 1000
}'

Get Projects ListGET

https://api.planhat.com/projects

When fetching multiple projects there are some options that can be used via query params:

  • companyId: Filter using company id.

  • limit: Limit the list length. Default as 100, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/projects?limit=2&offset=0&sort=-name&select=_id,name,companyId' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "60228bae4a93f65f056d2412",
        "companyId": "6022884d40bd001fb4b6e554",
        "name": "Älvdalen 2"
    },
    {
        "_id": "60228915f4d2e51f9cc29856",
        "companyId": "6022884d40bd001fb4b6e554",
        "name": "URL123"
    }
]'

Delete ProjectDELETE

https://api.planhat.com/projects/:_id

To delete an project it's required to pass the project _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/projects/6101a50072c0e0884d5e8db1' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert ProjectsPUT

https://api.planhat.com/projects

To create a project it's required define a name and a valid companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

To update a project it is required to specify in the payload one of the following keyables, listed in order of priority: _id, sourceId, externalId.

Since this is a bulk upsert operation it's possible create and/or update multiple projects with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/projects' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
                {
                    "_id": "6101a50072c0e0884d5e8db1",
                    "mrr": 10000
                },
                {
                    "companyId": "61006bc89a3e0b702ed8ea49",
                    "startDate": "2021-07-25T04:00:00.000Z",
                    "endDate": "2021-07-30T04:00:00.000Z",
                    "currency": "USD",
                    "mrr": 5000,
                    "name": "Improvements"
                }
            ]'

Example Response

200 OK

'{
    "created": 1,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "6101a93e72c0e0884d5e8e52"
        }
    ],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [
        "6101a93e72c0e0884d5e8e52"
    ],
    "permissionErrors": []
}'

Sale

https://api.planhat.com/sales

The Sale (NRR) model represents not recurring revenue, like an onboarding fee, or a one-off professional services project.

Property Required Type Description
_id
objectId Planhat identifier.
_currency
string The currency code, for example "USD". There is a "soft" mapping to the id’s in your list of currencies. Technically we accept any string but we recommend using standard currency codes as Ids. Since the mapping is soft you can add sales with a currency code not yet available in your list fo currencies.
product
string Name of the sales product. Not required but highly recommended. Even if you only have one product we suggest adding it as "Advanced Onboarding".
companyId
objectId Related company id (planhat identifier).
companyName
string Company name. (Autogenerated).
externalId
string The sale id in your own system.
sourceId
string The sale id from an integration (Sales Force, Hubspot, etc).
salesDate
string Date when sale was happened in ISO format.
value
number The total sales value, must be positive.
custom
object A flexible object with custom data.

Create SalePOST

https://api.planhat.com/sales

To create a sale the only real value that is required is the companyId but it doesn't make much sense to have a sale just with a companyId, that is why we suggest specify a value and _currency.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

Example Request

curl --location -g --request POST 'https://api.planhat.com/sales' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
                "product": "Advanced Onboarding",
                "_currency": "USD",
                "salesDate": "2021-07-28T00:00:00.000Z",
                "value": 10000,
                "companyId": "61006bc89a3e0b702ed8ea49"
            }'

Example Response

200 OK

'{
    "value": 10000,
    "_id": "61016c80675c1b871faf2d4f",
    "product": "Advanced Onboarding",
    "_currency": "USD",
    "salesDate": "2021-07-28T00:00:00.000Z",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "companyName": "Tenet",
    "__v": 0
}'

Update SalePUT

https://api.planhat.com/sales/:_id

To update a sale it's required to pass the sale _id in the request URL as a parameter.

Alternately it’s possible to update using the sale externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/sales/extid-{{externalId}}

or

https://api.planhat.com/sales/srcid-{{sourceId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/sales/61016c80675c1b871faf2d4f' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "mrr": 100000
    }'

Example Response

200 OK

'{
    "_id": "61016c80675c1b871faf2d4f",
    "value": 15000,
    "product": "Advanced Onboarding",
    "_currency": "USD",
    "salesDate": "2021-07-28T00:00:00.000Z",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "companyName": "Tenet",
    "__v": 0
}'

Get Sale by IDGET

https://api.planhat.com/sales/:_id

To get a specific sale it's required to pass the _id in the request URL as a parameter.

Alternately it's possible get a sale using its externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/sales/extid-{{externalId}}

or

https://api.planhat.com/sales/srcid-{{sourceId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/sales/61016c80675c1b871faf2d4f' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "value": 10000,
    "_id": "61016c80675c1b871faf2d4f",
    "product": "Advanced Onboarding",
    "_currency": "USD",
    "salesDate": "2021-07-28T00:00:00.000Z",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "companyName": "Tenet",
    "__v": 0
}'

Get Sales ListGET

https://api.planhat.com/sales

When fetching multiple sales there are some options that can be used via query params:

  • companyId: Filter using company id.

  • limit: Limit the list length. Default as 100, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/sales?limit=2&offset=0&sort=-value&select=_id,product,value,companyId' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "59e04a67b640bc9e7c71f4a0",
        "product": "UpStart Meeting",
        "value": 300000,
        "companyId": "56ccc2d39b760ff232295795"
    },
    {
        "_id": "59e04a44b640bc9e7c71f497",
        "product": "UpStart Meeting",
        "value": 300000,
        "companyId": "56ccc2d39b760ff232295795"
    }
]'

Delete SaleDELETE

https://api.planhat.com/sales/:_id

To delete a sale it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/sales/61016c80675c1b871faf2d4f' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert SalesPUT

https://api.planhat.com/sales

To create a sale it's required define a companyId, _currency and value.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

To update a sale it is required to specify in the payload one of the following keyables, listed in order of priority: _id, sourceId, externalId.

Since this is a bulk upsert operation it's possible create and/or update multiple sales with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/sales' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
                {
                    "product": "Advanced Onboarding",
                    "_currency": "USD",
                    "salesDate": "2021-07-28T00:00:00.000Z",
                    "value": 5000,
                    "companyId": "61006bc89a3e0b702ed8ea49"
                },
                {
                    "_id": "61016c80675c1b871faf2d4f",
                    "value": 200000
                }
            ]'

Example Response

200 OK

'{
    "created": 1,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "6101714572c0e0884d5e8160"
        }
    ],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [
        "6101714572c0e0884d5e8160"
    ],
    "permissionErrors": []
}'

Task

https://api.planhat.com/tasks

Tasks are the things that you plan to do in the future. It can be a simple "to-do" without any specific due date, a reminder of something to be done at a specific point in time, or even a meeting with a start and end time.

Most of the time these tasks will be automatically generated in Planhat based on rules you set up. It's also comon to have tasks as steps in a Playbook. But tasks can also be created ad-hoc just like you would in any task management app.

Tasks managed over the API should typically have the mainType property set to `task`, the other potential value is `event`, which indicates that it was synced to or from a calendar like Google Calendar. Though it's also possible to create tasks of type event in Planhat without syncing them back to any calendar.

Once a task is completed it's archived and genrally not visble in Planhat anymore. Sometimes when completing a tasks, say a training session, you want to log a note summarizing how it went, this is managed automatically by Planhat when working in the Planhat app.

Property Required Type Description
_id
objectId Planhat identifier.
mainType
string Required type should be always task.
companyId
objectId Related company id (planhat identifier).
companyName
string Company name. (Autogenerated).
startTime
string ISO date when the task will starts.
dateDone
string ISO date when the task was completed.
action
string Title of the task, e.g. "Send Invoice"
description
string Some description of the task.
repeat
string If task repeatable then should send how often. The options are: daily, weekly, monthly, quarterly, yearly and custom.
repeatDays
integer Number of days that the task will be repeated. Required if a custom repeat interval is set.
type
string Type of task.
ownerId
objectId ObjectId of the team member that owns the task.
ownerName
string Owner name of the task. (Autogenerated).
doneBy
objectId ObjectId of the team member who did he task.
sourceId
string The task id from an integration (Sales Force, Hubspot, etc).
noSpecificTime
boolean True if the task doesn't have any specific date to be executed. (Autogenerated).
shared
boolean True if the task is shared. (Autogenerated).
overdueNotified
boolean True if the task was notified due overdue. (Autogenerated).
activityTags
array Array of string to tags.
isArchived
boolean Flag the task as archived.
status
string Each task can be assigned a status in Planhat. It’s a text string that should match the name of one of the task statuses in Planhat. If the name doesn’t match any of the statuses in Planhat it will still be saved.
checklist
array Array of check list actions to be executed with the task.
snippet
string Formatted content of the task.
endusers
array Array of involved contacts. (Autogenerated).
users
array Array of involved users. (Autogenerated).
custom
object Custom object with your custom properties.
createdAt
string An automatically created field that holds the Task creation ISO date.
updatedAt
string Automatically created and updated field that holds the ISO date when the task was last modified.

Create TaskPOST

https://api.planhat.com/tasks

To create a task it is required a valid companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

If the mainType is not specified it will be created with the default value: "task"

It is recommended to set the action field, think of it as the tasks title e.g. "Send Invoice"

Example Request

curl --location -g --request POST 'https://api.planhat.com/tasks' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
    "ownerId": "58e231b14246fc73139f29e8",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "mainType": "task",
    "noSpecificTime": true,
    "startTime": "2021-07-29T00:00:00.00Z",
    "repeat": "daily",
    "action": "New task",
    "description": "

Call the client.

" }'

Example Response

200 OK

'{
    "mainType": "task",
    "noSpecificTime": true,
    "shared": false,
    "overdueNotified": false,
    "activityTags": [],
    "isArchived": false,
    "status": "In-progress",
    "_id": "6102c9748084189dcbf0df60",
    "ownerId": "58e231b14246fc73139f29e8",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "startTime": "2021-07-29T00:00:00.000Z",
    "repeat": "daily",
    "action": "New task",
    "description": "

Call the client.

", "snippet": "Call the client.", "companyName": "Tenet", "endusers": [], "users": [], "checklist": [], "__v": 0, "userEmail": "user@planhat.com", "createdAt": "2022-07-05T09:51:52.818Z", "updatedAt": "2022-07-05T09:51:52.818Z" }'

Update TaskPUT

https://api.planhat.com/tasks/:_id

To update an task it's required to pass the task _id in the request URL as a parameter.

Alternately it’s possible to update using the task sourceId adding a prefix and passing this keyable as identifier.

Example:

https://api.planhat.com/tasks/srcid-{{sourceId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/tasks/6102c9748084189dcbf0df60' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "action": "Daily contact",
        "sourceId": "sfdc-1234"
    }'

Example Response

200 OK

'{
    "mainType": "task",
    "noSpecificTime": true,
    "shared": false,
    "overdueNotified": false,
    "activityTags": [],
    "isArchived": false,
    "status": "In-progress",
    "_id": "6102c9748084189dcbf0df60",
    "ownerId": "58e231b14246fc73139f29e8",
    "companyId": "61006bc89a3e0b702ed8ea49",
    "startTime": "2021-07-29T00:00:00.000Z",
    "repeat": "daily",
    "action": "Daily contact",
    "description": "

Call the client.

", "snippet": "Call the client.", "companyName": "Tenet", "endusers": [], "users": [], "checklist": [], "__v": 0, "ownerName": "Alex", "sourceId": "sfdc-1234", "createdAt": "2022-07-05T09:51:52.818Z", "updatedAt": "2022-07-05T09:51:52.818Z" }'

Get Task by IDGET

https://api.planhat.com/tasks/:_id

To get a specific task it's required to pass the _id in the request URL as a parameter.

Alternately it's possible get a task using its sourceId adding a prefix and passing this keyable as identifier.

Example:

https://api.planhat.com/tasks/srcid-{{sourceId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/tasks/5db866ceab7aac67dc3637c8' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "5db866ceab7aac67dc3637c8",
    "mainType": "task",
    "noSpecificTime": true,
    "shared": false,
    "overdueNotified": false,
    "activityTags": [],
    "isArchived": false,
    "status": "In-progress",
    "action": "Sub activity",
    "description": "",
    "companyId": "56ccc2d39b760ff232295794",
    "companyName": "Grand Hotels",
    "createdAt": "2022-07-05T09:51:52.818Z",
    "updatedAt": "2022-07-05T09:51:52.818Z",
    "ownerId": "54ddc90a3570c76e2cbd08a5",
    "ownerName": "Niklas",
    "workflowId": "5db866ceab7aac67dc3637be",
    "workflowName": "QBR",
    "workflowTaskId": "5d27408853372f7cd1170407",
    "checklist": [],
    "parent": "5db866ceab7aac67dc3637c6",
    "sortPosition": 5,
    "snippet": "",
    "endusers": [],
    "users": [],
    "children": [],
    "comments": [],
    "__v": 0,
    "workflowTemplateId": "5a57d7754b5f1d13128a0533",
    "repeat": null,
    "custom": {
        "HScore": 0
    },
    "workflowStepId": "5db866ceab7aac67dc3637c8"
}'

Get Tasks ListGET

https://api.planhat.com/tasks

When fetching multiple tasks there are some options that can be used via query params:

  • companyId: Filter using company id. Multiple ids can be used separating them by commas.

  • limit: Limit the list length. Default as 500, max. 10000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

  • enduserIds: Filter using endusers id. Multiple ids can be used separating them by commas.

  • isArchived: Filter archived or not archived status. Default as false and this endpoint will returned objects where the mainType is `event` and `task`.

Example Request

curl --location -g --request GET 'https://api.planhat.com/tasks?limit=2&offset=0' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "5da4bbec7b1616495b53a35b",
        "mainType": "task",
        "noSpecificTime": true,
        "shared": false,
        "overdueNotified": false,
        "activityTags": [],
        "isArchived": false,
        "ownerId": "5524481f516d59216a6d2b80",
        "companyId": "56bccdf554d64d837d01be96",
        "companyName": "Daimler Group",
        "createdAt": "2022-07-05T09:51:52.818Z",
        "updatedAt": "2022-07-05T09:51:52.818Z",
        "startTime": null,
        "action": "Send them gift card",
        "ownerName": "Sarah",
        "endusers": [],
        "users": [],
        "children": [],
        "comments": [],
        "checklist": [],
        "__v": 0,
        "repeat": null,
        "custom": {
            "HScore": 0
        }
    },
    {
        "_id": "5db866ceab7aac67dc3637c8",
        "mainType": "task",
        "noSpecificTime": true,
        "shared": false,
        "overdueNotified": false,
        "activityTags": [],
        "isArchived": false,
        "action": "Sub activity",
        "description": "",
        "companyId": "56ccc2d39b760ff232295794",
        "companyName": "Grand Hotels",
        "ownerId": "54ddc90a3570c76e2cbd08a5",
        "ownerName": "Niklas",
        "workflowId": "5db866ceab7aac67dc3637be",
        "workflowName": "QBR",
        "workflowTaskId": "5d27408853372f7cd1170407",
        "checklist": [],
        "parent": "5db866ceab7aac67dc3637c6",
        "sortPosition": 5,
        "snippet": "",
        "endusers": [],
        "users": [],
        "children": [],
        "comments": [],
        "__v": 0,
        "workflowTemplateId": "5a57d7754b5f1d13128a0533",
        "repeat": null,
        "custom": {
            "HScore": 0
        },
        "workflowStepId": "5db866ceab7aac67dc3637c8"
    },
    {
        "_id": "5f05cc03da205d2dfb2ad775",
        "mainType": "event",
        "noSpecificTime": true,
        "shared": true,
        "overdueNotified": false,
        "activityTags": [],
        "isArchived": false,
        "action": "Test",
        "description": "",
        "companyId": "56bccdf554d64d837d01be55",
        "companyName": "Sweco",
        "ownerId": "564b063a508f068051ee82a5",
        "ownerName": "Diego",
        "workflowId": "5f05cc03da205d2dfb2ad773",
        "workflowName": "Churn Prevention",
        "workflowTaskId": "5ed7bba1492c366143976d13",
        "workflowTemplateId": "57fdd14e8be8ceb43f8b1bc6",
        "checklist": [
            {
                "title": "A"
            },
            {
                "title": "B"
            },
            {
                "title": "C"
            }
        ],
        "sortPosition": 1,
        "snippet": "",
        "endusers": [],
        "users": [],
        "children": [
            {
                "_id": "5f6889608baac92465787bf4"
            }
        ],
        "comments": [],
        "__v": 0,
        "custom": {
            "HScore": 0
        },
        "workflowStepId": "5f05cc03da205d2dfb2ad775"
    },
    {
        "_id": "5f317749604a74699f58f669",
        "mainType": "event",
        "noSpecificTime": true,
        "shared": false,
        "overdueNotified": false,
        "activityTags": [],
        "isArchived": false,
        "status": "In-progress",
        "companyId": "5942d6dfd4333a2f083011e8",
        "companyName": "iBM",
        "workflowId": "5f317728604a74699f58f65f",
        "workflowName": "Test Playbook",
        "workflowTemplateId": "5f317715d2b1cc0ebb619beb",
        "action": "Test Event",
        "description": "

Book a thing

Was it fun?

", "ownerId": "54ddc90a3570c76e2cbd08a5", "ownerName": "Niklas", "snippet": "Book a thing\n\nWas it fun?", "endusers": [], "users": [], "children": [], "comments": [], "checklist": [], "__v": 0, "custom": { "HScore": 0 }, "workflowStepId": "5f317749604a74699f58f669" } ]'

Delete TaskDELETE

https://api.planhat.com/tasks/:_id

To delete an task it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/tasks/6102c9748084189dcbf0df60' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{"n":1,"ok":1,"deletedCount":1}'

Bulk Upsert TaskPUT

https://api.planhat.com/tasks

To create an issue it's required define a mainType and companyIds.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

To update a task it is required to specify in the payload one of the following keyables, listed in order of priority: _id, sourceId.

If the mainType is not specified it will be created with the default value: "task"

It is recommended to set the action field, think of it as the tasks title e.g. "Send Invoice".

Since this is a bulk upsert operation it's possible create and/or update multiple tasks with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/tasks' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[{
          "mainType": "task",
          "startTime": "2022-07-05T14:02:16.181Z",
          "ownerId": "61f91285d40d7d112020c7b6",
          "ownerName": "Ernesto",
          "action": "First Onboard Call",
          "companyId": "5fd76c4daf9f4601dd313b05",
          "checklist": [{
                  "title": "Prepare Material"
              },
              {
                  "title": "Present"
              }
          ]
      },
      {
          "mainType": "task",
          "startTime": "2022-07-05T14:02:16.181Z",
          "ownerId": "61f91285d40d7d112020c7b6",
          "ownerName": "Ernesto",
          "action": "Send Invoice",
          "checklist": [],
          "companyId": "60c10846108183032f05bd67"
      },
      {
          "mainType": "task",
          "startTime": "2022-07-05T14:02:16.181Z",
          "ownerId": "61f91285d40d7d112020c7b6",
          "ownerName": "Ernesto",
          "companyId": "60c1075808f652259315e270",
          "action": "Creating Tasks in bulk",
          "description": "

Description

", "checklist": [{ "title": "Step 1" }, { "title": "Step 2" } ] } ]'

Example Response

200 OK

'{
    "created": 3,
    "createdErrors": [],
    "insertsKeys": [
      {
        "_id": "62c549dcfde99d59485c3314"
      },
      {
        "_id": "62c549dcfde99d59485c3315"
      },
      {
        "_id": "62c549dcfde99d59485c3316"
      }
    ],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [
      "62c549dcfde99d59485c3314",
      "62c549dcfde99d59485c3315",
      "62c549dcfde99d59485c3316"
    ],
    "permissionErrors": []
  }'

Ticket

https://api.planhat.com/tickets

Tickets in Planhat are Conversations, so if you plan to send tickets to Planhat via API then you can also use that endpoint. The ticket endpoint contains a bit of convenience logic for save tickets specificially, like setting the proper type automatically.

Most of our customers sync tickets from an external system like Zendesk or Salesforce. In case your ticketing system isn't natively supported or you have your own system for it, please let us know and we'll be happy to discuss how to best work with this api.

Property Required Type Description
_id
objectId Planhat identifier.
source
string The name of the system this ticket originates from. Typically "Zendesk”, "Desk etc, but since you’re reading these docs you may have your tickets in some other tool.
companyId
objectId Related company id (planhat identifier).
sourceId
string Required. Id of the ticket in the source system.
email
string Required. Email of the person who submitted the ticket. Items without email will be silently dropped.
domains
array Array of strings (required if companyExternalId not specified).
companyExternalId
string The External Company Id.
title
string If the ticket has a title or main subject.
description
string Description of the ticket.
url
string Url to where more information can be found about this ticket.
tags
array Array of tags in string format.
type
string the type of the ticket. Use as you like, but typically it could be: "bug", "feature request", "training" etc.
severity
string String describing the severity, no restrictions on the scale apart from that.
product
string Name of the product to which the ticket relates.
timeSpent
integer Time spent on this ticket, measured in number of minutes.
status
string Any status options you like. Typically "new", "pending", "open", "resolved", "closed" or something similar.
history
array Array of object containing the status history of the ticket.

Bulk Upsert TicketsPUT

To create a ticket it's required define a sourceId, email and domains.

To update a ticket it is required to specify in the payload one of the following keyables, listed in order of priority: _id, sourceId.

Since this is a bulk upsert operation it's possible create and/or update multiple tickets with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/tickets' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
        {
        "sourceId": "119",
        "source": "freshdesk",
        "status": "open",
        "history": [{"status": "open", "time": "2019-12-15T09:09:08.000Z"}],
        "title": "Test Ticket",
        "description": "Let have a chat?",
        "url": "http://urltoyourticket.com/119",
        "type": "ticket",
        "email": "ojpsoi57pzn@gmail.com",
        "name": "first-ojpsoi57pzn",
        "companyId": "61006bc89a3e0b702ed8ea49",
        "agentEmail": null,
        "domains": ["planhat.com", "google.com"]
        }
    ]'

Example Response

200 OK

'{
    "created": 1,
    "updated": 0
}'

Get Ticket ListGET

When fetching tickets there are some options that can be used via query params:

  • companyId: Filter using company id. Multiple ids can be used separating them by commas.

  • limit: Limit the list length. Default as 500, max. 10000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

  • status: Filter by status of tickets.

  • email: Filter by email.

  • search: Filter ticket searching for matching strings in the snippets.

Example Request

curl --location -g --request GET 'https://api.planhat.com/tickets?s=open&cid=61006bc89a3e0b702ed8ea49&l=1' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "60d4e139e9c01e379effa355",
        "externalId": "5003X00002CDLbHQAX",
        "type": "ticket",
        "source": "salesforce",
        "subType": "ticket",
        "subject": "[00001044] SFDC Case 1",
        "snippet": "Test case from SFDC",
        "email": "jbacon@gmail.com",
        "status": "New",
        "history": [
            {
                "status": "New",
                "time": "2021-06-24T19:47:05.084Z"
            }
        ],
        "url": "https://eu29.salesforce.com/5003X00002CDLbHQAX",
        "createDate": "2021-06-24T19:38:16.000Z",
        "updateDate": "2021-06-24T19:38:16.000Z",
        "date": "2021-06-24T19:38:16.000Z",
        "days": 18802,
        "timeBucket": [
            "2021",
            "2021-Q2",
            "2021-6",
            "2021-W26"
        ],
        "companyId": "56bccdf554d64d837d01be80",
        "companyName": "Exxon",
        "endusers": [
            {
                "id": "5f7e1a07dcd4235b544f3ce4",
                "name": "John Bacon"
            }
        ],
        "inDate": "2021-06-24T19:38:16.000Z",
        "custom": {
            "Closed Date": null,
            "Created Date": "2021-06-24T19:38:16.000+0000",
            "Days in Phase": 0,
            "Logins": 0,
            "Renewal in days": -1,
            "Activity Count": 0,
            "Week No": 29.285714285714285
        },
        "starred": false,
        "pinned": false,
        "activityTags": [],
        "emailTemplateIds": [],
        "isOpen": false,
        "tags": [],
        "waitsToBeFiltered": true,
        "archived": false,
        "users": [],
        "sender": []
    },
    {
        "_id": "5f579260f74efb6cfda04ba3",
        "externalId": "5003X00001xQsPZQA0",
        "type": "ticket",
        "source": "salesforce",
        "subType": "ticket",
        "subject": "[00001036] Case - High Prio",
        "snippet": "No enduser at first",
        "email": "sean@edge.com",
        "status": "Closed",
        "history": [
            {
                "status": "Escalated",
                "time": "2020-09-08T14:17:04.152Z"
            },
            {
                "status": "Escalated",
                "time": "2020-10-27T12:42:15.954Z"
            },
            {
                "status": "Escalated",
                "time": "2020-11-06T10:28:39.225Z"
            },
            {
                "status": "Escalated",
                "time": "2020-12-18T11:27:07.551Z"
            },
            {
                "status": "Closed",
                "time": "2020-12-18T11:29:53.022Z"
            },
            {
                "status": "Closed",
                "time": "2020-12-18T11:31:31.557Z"
            },
            {
                "status": "Closed",
                "time": "2020-12-18T11:32:41.930Z"
            },
            {
                "status": "Closed",
                "time": "2021-02-03T09:48:37.816Z"
            },
            {
                "status": "Closed",
                "time": "2021-04-09T13:03:03.578Z"
            }
        ],
        "url": "https://eu29.salesforce.com/5003X00001xQsPZQA0",
        "createDate": "2020-09-08T14:14:01.000Z",
        "updateDate": "2020-12-18T11:29:35.000Z",
        "date": "2020-12-18T11:29:35.000Z",
        "days": 18614,
        "timeBucket": [
            "2020",
            "2020-Q4",
            "2020-12",
            "2020-W51"
        ],
        "companyId": "56ccc2d39b760ff232295792",
        "companyName": "Edge Communications",
        "endusers": [
            {
                "id": "56ccc2d49b760ff23229579c",
                "name": "Sean Morty"
            }
        ],
        "inDate": "2020-12-18T11:29:35.000Z",
        "starred": false,
        "pinned": false,
        "activityTags": [],
        "emailTemplateIds": [],
        "isOpen": false,
        "tags": [],
        "waitsToBeFiltered": true,
        "archived": false,
        "users": [],
        "sender": [],
        "comments": [],
        "custom": {
            "t1": 0,
            "Created date": "2020-09-08T14:14:01.000+0000",
            "Closed Date": "2020-12-18T11:29:35.000+0000",
            "Status": "Open",
            "Created Date": "2020-09-08T14:14:01.000+0000",
            "HScore": 0,
            "Days in Phase": 0,
            "Team Attendees": [
                "58e231b14246fc73139f29e8"
            ],
            "NRR30": 0,
            "t123": "undefined",
            "Logins": 0,
            "Renewal in days": -1,
            "Activity Count": 0,
            "Week No": 29.285714285714285
        }
    }
]'

Delete TicketDELETE

https://api.planhat.com/tickets/:_id

To delete an ticket it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/tickets/:_id' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "starred": false,
    "pinned": false,
    "autoTags": [],
    "activityTags": [],
    "emailTemplateIds": [],
    "isOpen": false,
    "tags": [],
    "waitsToBeFiltered": true,
    "timeBucket": [
        "2021",
        "2021-Q2",
        "2021-6",
        "2021-W26"
    ],
    "archived": false,
    "_id": "60d4e139e9c01e379effa355",
    "externalId": "5003X00002CDLbHQAX",
    "type": "ticket",
    "source": "salesforce",
    "subType": "ticket",
    "subject": "[00001044] SFDC Case 1",
    "snippet": "Test case from SFDC",
    "email": "jbacon@gmail.com",
    "status": "New",
    "history": [
        {
            "status": "New",
            "time": "2021-06-24T19:47:05.084Z"
        }
    ],
    "url": "https://eu29.salesforce.com/5003X00002CDLbHQAX",
    "createDate": "2021-06-24T19:38:16.000Z",
    "updateDate": "2021-06-24T19:38:16.000Z",
    "date": "2021-06-24T19:38:16.000Z",
    "days": 18802,
    "companyId": "56bccdf554d64d837d01be80",
    "companyName": "Exxon",
    "endusers": [
        {
            "id": "5f7e1a07dcd4235b544f3ce4",
            "name": "John Bacon"
        }
    ],
    "inDate": "2021-06-24T19:38:16.000Z",
    "custom": {
        "Closed Date": null,
        "Created Date": "2021-06-24T19:38:16.000+0000",
        "Days in Phase": 0,
        "Logins": 0,
        "Renewal in days": -1,
        "Activity Count": 0,
        "Week No": 29.285714285714285
    },
    "users": [],
    "sender": []
}'

Time Entry

https://api.planhat.com/timeentries

The Time Entry model represents single time log, used for tracking time. Theoretically, Time Entries can be associated with many models in Planhat (all except User, Metric), and can be restricted to certain models in tenant settings. For now Time Entries created via API can only be associated to the Company model, by setting the CompanyId.

Property Required Type Description
_id
objectId Planhat identifier.
companyId
objectId Company id (planhat identifier). If time entry is tracked on model that is not company, this field is autofilled if model is related to company.
date
string Date when time was logged in ISO format.
hours
number The total hours logged, must be positive, can be a decimal number, and between 0.1 and 24.
isTimeOff
boolean Indicates if the time entry is a time off (vacation, sick leave, etc).
description
string Description of the time entry.
isBillable
boolean Indicates if the time entry is billable.
billingCode
string Billing code for the time entry if it is billable.
currency
string The currency code, for example "USD". There is a "soft" mapping to the id’s in your list of currencies. Technically we accept any string but we recommend using standard currency codes as Ids. Since the mapping is soft you can add time entries with a currency code not yet available in your list fo currencies.
billableRate
number Billable rate for the time entry.
totalBilled
number The total amount billed for the time entry (hours * billableRate). Calculated automatically.
costRate
number Cost rate for the time entry.
totalCost
number The total cost for the time entry (hours * costRate). Calculated automatically.
timesheetStatus
string Status of the time entry in the timesheet. Possible values are: "draft", "submitted", "approved", "returned". Default is "draft". It's readonly field. (Autogenerated).
timesheetId
objectId Related timesheet id (planhat identifier), exists only if the time entry belongs to timesheet. It's readonly field. (Autogenerated).
assignedModel
string Model which the Time Entry is assigned to. Current supported value is “User”.
assignedId
objectId Id of which the Time Entry is assigned to. Currently only support User Ids.

Create Time EntryPOST

https://api.planhat.com/timeentries

To create a time entry the required fields are date and hours. Keep in mind that the hours must be positive, can be a decimal number, and between 0.1 and 24.

Example Request

curl --location -g --request POST 'https://api.planhat.com/timeentries' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "hours": 5,
        "date": "2025-01-30T14:51:01.691Z",
        "description": "Worked on the integration issue",
    }'

Example Response

200 OK

'{
  "timesheetStatus": "draft",
  "assignedModel": "User",
  "_id": "67d04352557181002fd16424",
  "hours": 5,
  "date": "2025-01-30T00:00:00.000Z",
  "description": "Worked on the integration issue",
  "path": [],
  "totalBilled": 0,
  "totalCost": 0,
  "createdAt": "2025-03-11T14:06:10.722Z",
  "updatedAt": "2025-03-11T14:06:10.722Z",
  "__v": 0
}'

Update Time EntryPUT

https://api.planhat.com/timeentries/:_id

To update a time entry it's required to pass the time entry _id in the request URL as a parameter.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/timeentries/610411d5b046afb2109df12c' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "hours": 7
    }'

Example Response

200 OK

'{
    "_id": "67d04352557181002fd16424",
    "timesheetStatus": "draft",
    "assignedModel": "User",
    "hours": 7,
    "date": "2025-01-30T00:00:00.000Z",
    "description": "Worked on the integration issue",
    "path": [],
    "totalBilled": 0,
    "totalCost": 0,
    "createdAt": "2025-03-11T14:06:10.722Z",
    "updatedAt": "2025-03-11T16:50:26.953Z",
    "__v": 0
}'

Get Time Entry by IDGET

https://api.planhat.com/timeentries/:_id

To get a specific time entry it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request GET 'https://api.planhat.com/timeentries/67d04352557181002fd16424' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "67d04352557181002fd16424",
    "timesheetStatus": "draft",
    "assignedModel": "User",
    "hours": 7,
    "date": "2025-01-30T00:00:00.000Z",
    "description": "Worked on the integration issue",
    "path": [],
    "totalBilled": 0,
    "totalCost": 0,
    "createdAt": "2025-03-11T14:06:10.722Z",
    "updatedAt": "2025-03-11T16:50:26.953Z",
    "__v": 0
}'

Get Time Entries ListGET

https://api.planhat.com/timeentries

When fetching multiple time entries there are some options that can be used via query params:

  • companyId: Filter using company id.

  • limit: Limit the list length. Default as 100, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/timeentries?limit=2&offset=0&sort=-createdAt&select=_id,hours,date' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "67d04352557181002fd16424",
        "hours": 7,
        "date": "2025-01-30T00:00:00.000Z"
    },
    {
        "_id": "67c98759b874ce002f97cb48",
        "date": "2025-03-06T00:00:00.000Z",
        "hours": 12
    }
]'

Delete Time EntryDELETE

https://api.planhat.com/timeentries/:_id

To delete an time entry it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/timeentries/67d04352557181002fd16424' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert Time EntriesPUT

https://api.planhat.com/timeentries

To create an time entry it's required define a hours and date.

To update an time entry it is required to specify _id in the payload.

Since this is a bulk upsert operation it's possible create and/or update multiple time entries with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/timeentries' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
    {
        "_id": "677427729c28f8015843e87d",
        "hours": 6
    },
    {
        "_id": "677427729c28f8015843e87e",
        "description": "There is a problem with our integration settings that is affecting some users."
    },
    {
        "hours": 10,
        "date": "2025-01-30T14:51:01.691Z",
        "description": "Creating time entry using bulk operations"
    }
]'

Example Response

200 OK

'{
    "created": 1,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "67d070dc557181002fd16c9d"
        }
    ],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 2,
    "modified": [],
    "upsertedIds": [
        "67d070dc557181002fd16c9d"
    ],
    "permissionErrors": [],
    "validationErrors": []
}'

Duplicate Time EntryPOST

https://api.planhat.com/timeentries/duplicate

To duplicate a time entries it's required to pass the ids array in the payload. Bulk upsert will be performed on the time entries with the specified ids if ids exist.

Example Request

curl --location -g --request POST 'https://api.planhat.com/timeentries/duplicate' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "ids": ["67d07487557181002fd16d50", "677427729c28f8015843e87d", "677427729c28f8015843e87e"]
    }'

Example Response

200 OK

'{
    "count": 3,
    "errors": []
}'

Timesheet

https://api.planhat.com/timesheets

The Timesheet model represents collection of time entries for user, used for tracking time.

Property Required Type Description
_id
objectId Planhat identifier.
approvedBy
objectId User who approved the timesheet.
status
string Status of the timesheet. Possible values are: "submitted", "approved", "returned". Default is "submitted".
timeEntries
array Array of time entries associated with the timesheet.
dateOfApproval
string Date when the timesheet was approved.
dateFrom
number Start date of the timesheet period. Number of days since 1970-01-01.
dateTo
number End date of the timesheet period. Number of days since 1970-01-01.
assignedModel
string Model which the Timesheet is assigned to. Current supported value is “User”.
assignedId
objectId Id of which the Timesheet is assigned to. Currently only support User Ids.

Create TimesheetPOST

https://api.planhat.com/timesheets

To create a timesheet there are no required fields. The timesheet will be created with the status "submitted", but the date range should be provided in the request body.

Example Request

curl --location -g --request POST 'https://api.planhat.com/timesheets' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
    "timeEntries": [
        {
            "_id": "67d07487557181002fd16d50"
        },
        {
            "_id": "67d07694557181002fd16e1e"
        }
    ],
    "assignedId": "66eb28a6ef8857a5097fbce3",
    "assignedModel": "User",
    "dateFrom": 20156,
    "dateTo": 20163
}'

Example Response

200 OK

'{
    "timeEntries": [
        "67d07487557181002fd16d50",
        "67d07694557181002fd16e1e"
    ],
    "status": "submitted",
    "assignedModel": "User",
    "_id": "67d078fe557181002fd16f3e",
    "assignedId": "66eb28a6ef8857a5097fbce3",
    "dateFrom": 20156,
    "dateTo": 20163,
    "createdAt": "2025-03-11T17:55:10.313Z",
    "updatedAt": "2025-03-11T17:55:10.313Z",
    "__v": 0
}'

Update TimesheetPUT

https://api.planhat.com/timesheets/:_id

To update a timesheet it's required to pass the time entry _id in the request URL as a parameter.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/timesheets/67d078fe557181002fd16f3e' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "status": "approved"
    }'

Example Response

200 OK

'{
    "_id": "67d078fe557181002fd16f3e",
    "timeEntries": [
        "67d07487557181002fd16d50",
        "67d07694557181002fd16e1e"
    ],
    "status": "approved",
    "assignedModel": "User",
    "assignedId": "66eb28a6ef8857a5097fbce3",
    "dateFrom": 20159,
    "dateTo": 20165,
    "createdAt": "2025-03-11T17:55:10.313Z",
    "updatedAt": "2025-03-11T17:57:12.306Z",
    "__v": 0,
    "approvedBy": "66eb28a6ef8857a5097fbce3",
    "dateOfApproval": "2025-03-11T17:57:12.305Z"
}'

Get Timesheet by IDGET

https://api.planhat.com/timesheets/:_id

To get a specific timesheet it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request GET 'https://api.planhat.com/timesheets/67aca36fa8ba08053dee2ebf' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "67aca36fa8ba08053dee2ebf",
    "timeEntries": [
        "67aca36ea8ba08053dee2e73",
        "67aca36ea8ba08053dee2e74",
        "67aca36ea8ba08053dee2e75"
    ],
    "status": "submitted",
    "assignedModel": "User",
    "assignedId": "66eb28a6ef8857a5097fbce3",
    "dateFrom": 20128,
    "dateTo": 20135,
    "createdAt": "2025-02-12T13:34:39.353Z",
    "updatedAt": "2025-02-12T13:34:39.353Z",
    "__v": 0
}'

Get Timesheet ListGET

https://api.planhat.com/timesheets

When fetching multiple timesheets there are some options that can be used via query params:

  • limit: Limit the list length. Default as 100, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/timesheets?limit=2&offset=0&select=_id,dateFrom,dateTo' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "679a8fe00a84a200cd465542",
        "dateFrom": 20107,
        "dateTo": 20114
    },
    {
        "_id": "67a266e7c3074c019778e17c",
        "dateFrom": 20121,
        "dateTo": 20128
    }
]'

Delete TimesheetDELETE

https://api.planhat.com/timesheets/:_id

To delete an timesheet it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/timesheets/679a8fdd0a84a200cd4654aa' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert TimesheetsPUT

https://api.planhat.com/timesheets

To create an timesheet, there's no required field.

To update an timesheet it is required to specify _id in the payload.

Since this is a bulk upsert operation it's possible create and/or update multiple timesheets with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/timesheets' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
                {
                    "assignedId": "66eb28a6ef8857a5097fbce3",
                    "assignedModel": "User"
                },
                {
                    "_id": "67aca36fa8ba08053dee2ebf",
                    "status": "returned"
                },
                {
                    "_id": "67bf484af303d806b63d9a49",
                    "status": "submitted"
                }
            ]'

Example Response

200 OK

'{
    "created": 1,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "67d08140557181002fd170dd"
        }
    ],
    "updated": 1,
    "updatedErrors": [],
    "updatesKeys": [
        {
            "_id": "67aca36fa8ba08053dee2ebf"
        }
    ],
    "nonupdates": 1,
    "modified": [
        "67aca36fa8ba08053dee2ebf"
    ],
    "upsertedIds": [
        "67d08140557181002fd170dd"
    ],
    "permissionErrors": [],
    "validationErrors": []
}'

User

https://api.planhat.com/users

Users are all your team members that need access to Planhat. Users can be created in the app, using spreadsheet upload or over api.

If a user is flagged as inactive, they will not be able to login to Planhat and they will not get notifications, but they will be available for assigning accounts etc.

Property Required Type Description
_id
objectId Planhat identifier.
email
string Email of the user.
nickName
string Nickname of user which will be shown in Planhat
firstName
string First name of user.
lastName
string Last name of user.
roles
array Array of role ids assigned to the user.
externalId
string The users' id in your system.
inactive
boolean Sets the user in inactive state. A user set as inactive won't to able to login and won't receive notifications. Default as true.
image
object Object containing the avatar information. (Autogenerated).
radarOneLine
boolean Internal system field. (Autogenerated).
taskFilter
object Object containing the task filter information. (Autogenerated).
companyFilter
string Internal system field. (Autogenerated).
compressedView
boolean Internal system field. (Autogenerated).
isHidden
boolean Defines if the user is hidden within the syteam.
notificationsEnabled
boolean Defines if the user has notifications enabled. Default as true.
createDate
string ISO date of when the user was created. (Autogenerated).
permissions
array Array of user's permissions. (Autogenerated).
custom
object A flexible object with custom data.

Create UserPOST

https://api.planhat.com/users

To create an user it's required define in the payload a nickName, email, firstName and lastName.

Example Request

curl --location -g --request POST 'https://api.planhat.com/users' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "email": "bob@test.com",
        "firstName": "Bob",
        "lastName": "Marley",
        "nickName": "Bob",
        "roles": [
            "57e2b92ff795575a5b8c4659"
        ]
    }'

Example Response

200 OK

'{
    "skippedGettingStartedSteps": {
        "email": false,
        "linkedin": false,
        "avatar": false,
        "all": false,
        "team": false,
        "customers": false
    },
    "sharedNotificationsPrefs": {
        "enabled": [],
        "disabled": [],
        "disabledEvents": []
    },
    "image": {
        "path": ""
    },
    "firstName": "Bob",
    "lastName": "Marley",
    "isHidden": false,
    "removed": false,
    "inactive": true,
    "compressedView": false,
    "companyFilter": "",
    "taskFilter": "",
    "workflowFilter": "",
    "playLogDisabled": true,
    "radarOneLine": false,
    "expandedFolders": [],
    "usageReportColumnsEnabled": [],
    "companyUsersEnabledColumns": [],
    "revReportPeriodType": "past x days",
    "splitLayoutDisabled": false,
    "dailyDigest": true,
    "followerUpdate": true,
    "inAppNotifications": true,
    "lastVisitedCompanies": [],
    "lastVisitedEndusers": [],
    "roles": [
        "57e2b92ff795575a5b8c4659"
    ],
    "apiTokens": [],
    "poc": [],
    "isExposedAsSenderOption": false,
    "defaultMeetingLength": 60,
    "collapsedFolders": [],
    "type": "user",
    "_id": "61031e2ad414d49dcdb61b22",
    "email": "bob@test.com",
    "nickName": "Bob",
    "createDate": "2021-07-29T21:31:22.042Z",
    "__v": 0
}'

Update UserPUT

https://api.planhat.com/users/:_id

To update an user it's required to pass the user _id in the request URL as a parameter.

Alternately it’s possible to update using the user externalId adding a prefix.

Example:

https://api.planhat.com/users/extid-{{externalId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/users/61031e2ad414d49dcdb61b22' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "nickName": "Bob Marley"
    }'

Example Response

200 OK

'{
    "_id": "61031e2ad414d49dcdb61b22",
    "skippedGettingStartedSteps": {
        "email": false,
        "linkedin": false,
        "avatar": false,
        "all": false,
        "team": false,
        "customers": false
    },
    "sharedNotificationsPrefs": {
        "enabled": [],
        "disabled": [],
        "disabledEvents": []
    },
    "image": {
        "path": ""
    },
    "firstName": "Bob",
    "lastName": "Marley",
    "isHidden": false,
    "removed": false,
    "inactive": true,
    "compressedView": false,
    "companyFilter": "",
    "taskFilter": "",
    "workflowFilter": "",
    "playLogDisabled": true,
    "radarOneLine": false,
    "expandedFolders": [],
    "usageReportColumnsEnabled": [],
    "companyUsersEnabledColumns": [],
    "revReportPeriodType": "past x days",
    "splitLayoutDisabled": false,
    "dailyDigest": true,
    "followerUpdate": true,
    "inAppNotifications": true,
    "lastVisitedCompanies": [],
    "lastVisitedEndusers": [],
    "roles": [
        "57e2b92ff795575a5b8c4659"
    ],
    "poc": [],
    "isExposedAsSenderOption": false,
    "defaultMeetingLength": 60,
    "collapsedFolders": [],
    "type": "user",
    "email": "bob@test.com",
    "nickName": "Bob Marley",
    "createDate": "2021-07-29T21:31:22.042Z",
    "__v": 0
}'

Get User by IDGET

https://api.planhat.com/users/:_id

To get a specific user it's required to pass the _id in the request URL as a parameter.

Alternately it's possible get an user using its externalId adding a prefix.

Example:

https://api.planhat.com/users/extid-{{externalId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/users/564b063a508f068051ee82a5' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "564b063a508f068051ee82a5",
    "nickName": "Diego Checa",
    "email": "diego@planhat.com",
    "image": {
        "path": ""
    },
    "radarOneLine": false,
    "taskFilter": "",
    "compressedView": false,
    "isHidden": false,
    "createDate": "2016-02-11T18:07:49.173Z",
    "lastName": "",
    "firstName": "Diego",
    "__v": 13,
    "dailyDigest": true,
    "followerUpdate": true,
    "permissions": {
        "admin": true,
        "changeOwner": true,
        "onlyMyPortfolio": false,
        "developer": true
    },
    "revReportPeriodType": "past x days",
    "inAppNotifications": true,
    "playlistSection": "all",
    "lastDailyDigest": 18830,
    "accountAccess": "all accounts",
    "roles": [
        {
            "_id": "000000010000000000000000",
            "name": "CSM",
            "description": "Access to a few selected views and relevant accounts",
            "__v": 393
        },
        {
            "_id": "000000030000000000000000",
            "name": "Developer",
            "description": "Access to API keys, error pages etc",
            "__v": 4
        },
        {
            "_id": "000000000000000000000000",
            "name": "Administrator",
            "description": "Full access, including ability to edit users and settings",
            "__v": 55
        },
        {
            "_id": "000000020000000000000000",
            "name": "Manager",
            "description": "Access to the entire portfolio, and all functionality",
            "__v": 56
        }
    ],
    "msApi": {
        "accessEnabled": false,
        "syncEnabled": false,
        "syncInitial": false,
        "syncedLabels": []
    },
    "playLogDisabled": true,
    "poc": [
        "Agreement"
    ],
    "emailSignature": {
        "content": "
hello
", "enabled": false }, "inactive": false, "removed": false, "tzOffset": -5, "intercomAdminId": "1123812", "custom": {}, "defaultMeetingLength": 60, "isExposedAsSenderOption": false, "splitLayoutDisabled": false, "recentOpenSharedView": "56bccdf554d64d837d01be63", "invoicesSort": "-dueDate", "type": "user", "undefined": null, "googleApi": { "accessEnabled": false, "syncEnabled": false, "syncInitial": false, "syncedLabels": [] }, "googleCalendarApi": { "accessEnabled": false, "syncEnabled": false, "syncInitial": false, "syncedCalendars": [], "calendarToSave": {} }, "msCalendarApi": { "accessEnabled": false, "syncEnabled": false, "syncInitial": false, "syncedCalendars": [], "calendarToSave": {} } }'

Get Users ListGET

https://api.planhat.com/users

When fetching multiple projects there are some options that can be used via query params:

  • limit: Limit the list length. Default as 10000, max. 10000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/users' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "564b063a508f068051ee82a5",
        "nickName": "Diego Checa",
        "email": "diego@planhat.com",
        "image": {
            "path": ""
        },
        "radarOneLine": false,
        "taskFilter": "",
        "compressedView": false,
        "isHidden": false,
        "createDate": "2016-02-11T18:07:49.173Z",
        "lastName": "",
        "firstName": "Diego",
        "__v": 13,
        "dailyDigest": true,
        "followerUpdate": true,
        "permissions": {
            "admin": true,
            "changeOwner": true,
            "onlyMyPortfolio": false,
            "developer": true
        },
        "revReportPeriodType": "past x days",
        "inAppNotifications": true,
        "playlistSection": "all",
        "lastDailyDigest": 18830,
        "accountAccess": "all accounts",
        "roles": [
            {
                "_id": "000000010000000000000000",
                "name": "CSM",
                "description": "Access to a few selected views and relevant accounts",
                "__v": 393
            },
            {
                "_id": "000000030000000000000000",
                "name": "Developer",
                "description": "Access to API keys, error pages etc",
                "__v": 4
            },
            {
                "_id": "000000000000000000000000",
                "name": "Administrator",
                "description": "Full access, including ability to edit users and settings",
                "__v": 55
            },
            {
                "_id": "000000020000000000000000",
                "name": "Manager",
                "description": "Access to the entire portfolio, and all functionality",
                "__v": 56
            }
        ],
        "msApi": {
            "accessEnabled": false,
            "syncEnabled": false,
            "syncInitial": false,
            "syncedLabels": []
        },
        "playLogDisabled": true,
        "poc": [
            "Agreement"
        ],
        "emailSignature": {
            "content": "
hello
", "enabled": false }, "inactive": false, "removed": false, "tzOffset": -5, "intercomAdminId": "1123812", "custom": {}, "defaultMeetingLength": 60, "isExposedAsSenderOption": false, "splitLayoutDisabled": false, "recentOpenSharedView": "56bccdf554d64d837d01be63", "invoicesSort": "-dueDate", "type": "user", "undefined": null, "googleApi": { "accessEnabled": false, "syncEnabled": false, "syncInitial": false, "syncedLabels": [] }, "googleCalendarApi": { "accessEnabled": false, "syncEnabled": false, "syncInitial": false, "syncedCalendars": [], "calendarToSave": {} }, "msCalendarApi": { "accessEnabled": false, "syncEnabled": false, "syncInitial": false, "syncedCalendars": [], "calendarToSave": {} } }, { "_id": "5524481f516d59216a6d2b80", "nickName": "Sarah", "email": "sarah@planhat.com", "image": { "spacesKey": "arrivato/team/5524481f516d59216a6d2b80_1527055421955.png", "path": "" }, "radarOneLine": false, "taskFilter": "{\"owner\":\"5524481f516d59216a6d2b80\"}", "compressedView": false, "isHidden": false, "createDate": "2016-02-11T18:07:49.171Z", "lastName": "Jones", "firstName": "Sarah", "__v": 2, "permissions": { "admin": true, "changeOwner": true, "onlyMyPortfolio": false, "developer": false }, "segment": null, "playlistStyle": "calendar", "followerUpdate": false, "revReportPeriodType": "static", "revReportStartDay": 90, "revReportStartDate": "2020-08-01T00:00:00.000Z", "playlistSection": "all", "dailyDigest": true, "bubbleChartXParam": "time-to-renewal", "inAppNotifications": true, "revReportEndDate": "2020-09-01T00:00:00.000Z", "convReportEndDate": "2019-01-24T23:59:59.999Z", "convReportStartDate": "2018-10-26T23:00:00.000Z", "lastDailyDigest": 18831, "accountAccess": "all accounts", "googleApi": { "accessEnabled": false, "syncEnabled": false, "syncInitial": false, "syncedLabels": [] }, "msApi": { "accessEnabled": false, "syncEnabled": false, "syncInitial": false, "syncedLabels": [] }, "playLogDisabled": true, "roles": [ { "_id": "000000020000000000000000", "name": "Manager", "description": "Access to the entire portfolio, and all functionality", "__v": 56 }, { "_id": "000000000000000000000000", "name": "Administrator", "description": "Full access, including ability to edit users and settings", "__v": 55 }, { "_id": "000000010000000000000000", "name": "CSM", "description": "Access to a few selected views and relevant accounts", "__v": 393 } ], "custom": { "Role": "CSM" }, "disableIncludeChildrenPref": false, "inactive": false, "isExposedAsSenderOption": true, "poc": [], "removed": false, "tzOffset": 1, "splitLayoutDisabled": false, "intercomAdminId": "1019963", "recentOpenSharedView": "56bccdf554d64d837d01be9d", "opportunitiesSort": "landingDate", "opportunitiesColumnPref": { "companyName": true, "title": false, "Status": false, "comment": false, "ownerId": true, "mrr": false, "dealDate": false, "custom-% to Close": false, "custom-Owner": true, "custom-Technical Rep": true }, "issuesColumnPref": { "title": true, "createdAt": true, "status": true, "companies": true, "custom-Priority": true, "endusers": false, "description": false, "source": false, "sourceId": false, "projectId": false, "issueType": false, "priority": false, "sourceUrl": false, "updatedAt": false, "sourceKey": false }, "defaultMeetingLength": 60, "type": "user", "undefined": null, "googleCalendarApi": { "accessEnabled": false, "syncEnabled": false, "syncInitial": false, "syncedCalendars": [], "calendarToSave": {} }, "msCalendarApi": { "accessEnabled": false, "syncEnabled": false, "syncInitial": false, "syncedCalendars": [], "calendarToSave": {} } } ]'

Delete UserDELETE

https://api.planhat.com/users/:_id

To delete an user it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/users/61031e2ad414d49dcdb61b22' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{}'

Bulk Upsert UsersPUT

https://api.planhat.com/users

To create an enduser it's required define a nickName, email, firstName and lastName.

To update an enduser it is required to specify in the payload one of the following keyables, listed in order of priority: _id, externalId, email.

Since this is a bulk upsert operation it's possible create and/or update multiple users with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/users' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
        {
            "email": "rob@test.com",
            "firstName": "Robbert",
            "lastName": "Rob",
            "nickName": "Tyler",
            "roles": [
                "57e2b92ff795575a5b8c4659"
            ]
        },
        {
            "email": "liz@test.com",
            "firstName": "Liz",
            "lastName": "Elizabeth",
            "nickName": "Gates",
            "roles": [
                "5d35c292344cb854c0a5af28"
            ]
        }
    ]'

Example Response

200 OK

'{
    "created": 2,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "61033193e41b0aa81c926c8b",
            "email": "rob@test.com"
        },
        {
            "_id": "61033193e41b0aa81c926c8c",
            "email": "liz@test.com"
        }
    ],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [
        "61033193e41b0aa81c926c8b",
        "61033193e41b0aa81c926c8c"
    ],
    "permissionErrors": []
}'

Workspace

https://api.planhat.com/workspaces

If you work with sub-instances at your customers, e.g., connecting with different departments or with different versions of your product (think like a Workspace in Slack), then this is the object to track that engagement!

Property Required Type Description
_id
objectId Planhat identifier.
name
string Workspace name.
ownerId
objectId Related owner id (planhat identifier).
companyId
objectId Related company id (planhat identifier).
companyName
string Company name. (Autogenerated).
externalId
string The workspace id in your own system.
sourceId
string The workspace id from an integration (Sales Force, Hubspot, etc).
custom
object Custom object with your custom properties.

Create WorkspacePOST

https://api.planhat.com/workspaces

To create a workspace it's required define a name and a valid companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

Example Request

curl --location -g --request POST 'https://api.planhat.com/workspaces' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
    "name": "Workspace name",
    "companyId": "60df26bfa259250cba9cf816",
    "ownerId": "60df26bfa259250cba8cf310",
    "externalId": 1234,
    "sourceId": "sfc-1234",
    "custom": {
        "field": "custom field"
    }
}'

Example Response

200 OK

'{
    "_id": "60faeda853b8f717ebe36146",
    "name": "Workspace name",
    "companyId": "60df26bfa259250cba9cf816",
    "ownerId": "60df26bfa259250cba8cf310",
    "externalId": "1234",
    "sourceId": "sfc-1234",
    "custom": {
        "field": "custom field"
    },
    "companyName": "Test Company"
}'

Update WorkspacePUT

https://api.planhat.com/workspaces/:_id

To update a workspace it's required to pass the _id in the request URL as a parameter.

Alternately it’s possible to update using the workspace externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/workspaces/extid-{{externalId}}

or

https://api.planhat.com/workspaces/srcid-{{sourceId}}

Example Request

curl --location -g --request PUT 'https://api.planhat.com/workspaces/60faeda853b8f717ebe36146' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '{
        "name": "Workspace name 2"
    }'

Example Response

200 OK

'{
    "_id": "60ff061d681a6b4da9248694",
    "name": "Workspace name 2",
    "companyId": "60df26bfa259250cba9cf816",
    "ownerId": "60df26bfa259250cba8cf310",
    "externalId": "12323",
    "companyName": "Test Company"
    "__v": 0,
    "sourceId": "12323"
}'

Get Workspaces by IDGET

https://api.planhat.com/workspaces/:_id

To get a specific workspace it's required to pass the _id in the request URL as a parameter.

Alternately it's possible to get a workspace using its externalId and/or sourceId adding a prefix and passing one of these keyables as identifiers.

Example:

https://api.planhat.com/workspaces/extid-{{externalId}}

or

https://api.planhat.com/workspaces/srcid-{{sourceId}}

Example Request

curl --location -g --request GET 'https://api.planhat.com/workspaces/5ffcce42ad67267f66741147' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "_id": "5ffcce42ad67267f66741147",
    "companyId": "56bccdf554d64d837d01be9d",
    "ownerId": "60df26bfa259250cba8cf310",
    "companyName": "Tenet",
    "name": "Gold workspace",
    "__v": 0,
    "custom": {
        "Comments": "",
        "Main Product": [
            "Recipe Database"
        ],
        "Days in Phase": 0,
        "NRR30": 0,
        "t123": "undefined",
        "Logins": 0,
        "Overall Outlet Health": 4,
        "Kary FF": null
    },
    "externalId": "4467",
    "usage": {
        "6053b728fa96fa0262729a3d": 0,
        "6053ba62ca3eaf023a54d268": 0
    }
}'

Get Workspaces ListGET

https://api.planhat.com/workspaces

When fetching multiple workspaces there are some options that can be used via query params:

  • companyId: Filter using company id.

  • limit: Limit the list length. Default as 100, max. 2000.

  • offset: Start the list on a specific integer index.

  • sort: Sort based on a specific property. Prefix the property "-" to change the sort order.

  • select: Select specific properties. Multiple properties can be specified separating them by commas.

Example Request

curl --location -g --request GET 'https://api.planhat.com/workspaces?limit=2&offset=0&sort=-name&select=_id,name,companyId,ownerId' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'[
    {
        "_id": "60fb0869694ea374023924cb",
        "companyId": "56bccdf554d64d837d01be4a",
        "name": "ZPA Product",
        "ownerId": "60df26bfa259250cba8cf310"
    },
    {
        "_id": "601c0a253e5ed41388982528",
        "companyId": "6011889f52181c5640bf41ba",
        "name": "Trello.io",
        "ownerId": "60df26bfa259250cba8cf310"
    }
]'

Delete WorkspaceDELETE

https://api.planhat.com/workspaces/:_id

To delete a workspace it's required to pass the _id in the request URL as a parameter.

Example Request

curl --location -g --request DELETE 'https://api.planhat.com/workspaces/60faeda853b8f717ebe36146' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}'

Example Response

200 OK

'{
    "n": 1,
    "ok": 1,
    "deletedCount": 1
}'

Bulk Upsert WorkspacesPUT

https://api.planhat.com/workspaces

To create a workspace it's required define a name and a valid companyId.

You can instead reference the company externalId or sourceId using the following command structure: "companyId": "extid-[company externalId]" or "companyId": "srcid-[company sourceId]".

To update a workspace it is required to specify in the payload one of the following keyables, listed in order of priority: _id, sourceId, externalId.

Since this is a bulk upsert operation it's possible create and/or update multiple workspaces with the same payload.

For more details please refer to the bulk upsert section.

Note: There is an upper limit of 5,000 items per request.

Example Request

curl --location -g --request PUT 'https://api.planhat.com/workspaces' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{apiToken}}' \
--data-raw '[
        {
            "name": "Workspace name",
            "companyId": "60df26bfa259250cba9cf816",
            "ownerId": "60df26bfa259250cba8cf310",
            "externalId": 1234,
            "sourceId": "sfc-1234",
            "custom": {
                "field": "custom field"
            }
        },

        {
            "name": "Workspace name 2",
            "companyId": "60df26bfa259250cba9cf816",
            "ownerId": "60df26bfa259250cba8cf310",
            "externalId": 1235,
            "sourceId": "sfc-1235",
            "custom": {
                "field": "custom field"
            }
        }
    ]'

Example Response

200 OK

'{
    "created": 2,
    "createdErrors": [],
    "insertsKeys": [
        {
            "_id": "60fde9ad57df5b411cf39be5",
            "sourceId": "sfc-1234",
            "externalId": "1234"
        },
        {
            "_id": "60fde9ad57df5b411cf39be6",
            "sourceId": "sfc-1235",
            "externalId": "1235"
        }
    ],
    "updated": 0,
    "updatedErrors": [],
    "updatesKeys": [],
    "nonupdates": 0,
    "modified": [],
    "upsertedIds": [
        "60fde9ad57df5b411cf39be5",
        "60fde9ad57df5b411cf39be6"
    ],
    "permissionErrors": []
}'