A quick article because I couldn't find anything that documented this with a working example.
Why?
The use-case here is that we want to make a ZohoCreator page display events for this month from ZohoProjects.
How?
The bit that stumped me was making a call to the Events endpoint as it would just come back with a blank response...
The reason it was blank was due to my parameters; the key item to remember is that status is a mandatory field and it can either be "closed" or "open". If you have used ZohoCRM then this is obvious; if you haven't then it may be difficult to work this out. Put simply, "Closed" are events in the past and "Open" are events in the future. The "is_open" in tasks is misleading and cannot be used in the request for events. There are hardly any parameters you can send to. Here's some code to get the last 50 events:
// // *********************************** // get Zoho Projects information v_BaseURL = "https://projectsapi.zoho.com"; v_Heading = ""; // // get all portals v_EndpointPortals = v_BaseURL + "/restapi/portals/"; r_Portals = invokeUrl [ url: v_EndpointPortals type: GET connection: "my_projects_connection" ]; info r_Portals; // // once we check the JSON, we can select a portal name and ID, this avoids the need of using up an API request to get the portal v_PortalName = "my_portal"; v_PortalID = "789456123"; // // get all projects v_EndpointProjects = v_BaseURL + "/restapi/portal/" + v_PortalID + "/projects/"; r_Projects = invokeUrl [ url: v_EndpointProjects type: GET connection: "my_projects_connection" ]; // // already decided as we only want one v_ProjectName = "My Project Board"; v_ProjectID = "1234567000001234567"; // // get all users (optional here) v_EndpointUsers = v_BaseURL + "/restapi/portal/" + v_PortalID + "/projects/" + v_ProjectID + "/users/"; r_Users = invokeurl [ url :v_EndpointUsers type :GET connection:"my_projects_connection" ]; // // get all events // NB: status=open are upcoming events; status=closed are events in the past; status is a mandatory field // NB: range is an integer and the number of records to return (default=100; max=200) // NB: index is an integer and is the offset or starting index (not the ID of the record) m_Params = Map(); m_Params.put("range",50); m_Params.put("status","closed"); v_EndpointEvents = v_BaseURL + "/restapi/portal/" + v_PortalID + "/projects/" + v_ProjectID + "/events/"; r_PastEvents = invokeurl [ url :v_EndpointEvents type :GET parameters:m_Params connection:"my_projects_connection" ]; l_PastEvents = ifnull(r_PastEvents.get("events"),List()); for each m_PastEvent in l_PastEvents { // // convert a Zoho Project DateTime to a DateTime datatype (given 12/12/2023 12:00:00 PM... remove ambiguity: mm/dd or dd/mm?) if(!isNull(m_PastEvent.get("scheduled_on"))) { if(m_PastEvent.get("scheduled_on").contains(" ")) { l_ThisDateTimeEventParts = m_PastEvent.get("scheduled_on").toList(" "); if(l_ThisDateTimeEventParts.get(0).contains("/")) { l_ThisDateEventParts = l_ThisDateTimeEventParts.get(0).toList("/"); v_ThisDateEvent_Year = l_ThisDateEventParts.get(2); v_ThisDateEvent_Month = l_ThisDateEventParts.get(1); v_ThisDateEvent_Day = l_ThisDateEventParts.get(0); l_ThisTimeEventParts = l_ThisDateTimeEventParts.get(1).toList(":"); v_ThisDateEvent_Minute = l_ThisTimeEventParts.get(1); v_ThisDateEvent_Hour = l_ThisTimeEventParts.get(0); // // convert 12-hour to 24-hour v_ThisDateEvent_Hour = if(l_ThisDateTimeEventParts.get(2)=="PM", v_ThisDateEvent_Hour.toLong() + 12, v_ThisDateEvent_Hour); v_ThisDateEvent_Hour = if(v_ThisDateEvent_Hour.toLong()==24, "12", v_ThisDateEvent_Hour); // v_ThisEventDate = v_ThisDateEvent_Year + "-" + v_ThisDateEvent_Month + "-" + v_ThisDateEvent_Day; v_ThisEventDateSort = v_ThisDateEvent_Year.toString() + v_ThisDateEvent_Month.toString() + v_ThisDateEvent_Day.toString() + v_ThisDateEvent_Hour.toString() + v_ThisDateEvent_Minute.toString() + ifnull(m_PastEvent.get("title"),"?").trim().toLowerCase(); v_ThisEventStart = v_ThisDateEvent_Year + "-" + v_ThisDateEvent_Month + "-" + v_ThisDateEvent_Day + " " + v_ThisDateEvent_Hour + ":" + v_ThisDateEvent_Minute + ":00"; // // date and time obtained... do your other stuff here } } } } // // get all future events // NB: status=open are upcoming events; status=closed are events in the past; status is a mandatory field // NB: range is an integer and the number of records to return (default=100; max=200) // NB: index is an integer and is the offset or starting index (not the ID of the record) m_Params = Map(); m_Params.put("range",50); m_Params.put("status","open"); v_EndpointEvents = v_BaseURL + "/restapi/portal/" + v_PortalID + "/projects/" + v_ProjectID + "/events/"; r_FutureEvents = invokeurl [ url :v_EndpointEvents type :GET parameters:m_Params connection:"my_projects_connection" ]; l_FutureEvents = ifnull(r_FutureEvents.get("events"),List());
- //
- // ***********************************
- // get Zoho Projects information
- v_BaseURL = "https://projectsapi.zoho.com";
- v_Heading = "";
- //
- // get all portals
- v_EndpointPortals = v_BaseURL + "/restapi/portals/";
- r_Portals = invokeUrl
- [
- url: v_EndpointPortals
- type: GET
- connection: "my_projects_connection"
- ];
- info r_Portals;
- //
- // once we check the JSON, we can select a portal name and ID, this avoids the need of using up an API request to get the portal
- v_PortalName = "my_portal";
- v_PortalID = "789456123";
- //
- // get all projects
- v_EndpointProjects = v_BaseURL + "/restapi/portal/" + v_PortalID + "/projects/";
- r_Projects = invokeUrl
- [
- url: v_EndpointProjects
- type: GET
- connection: "my_projects_connection"
- ];
- //
- // already decided as we only want one
- v_ProjectName = "My Project Board";
- v_ProjectID = "1234567000001234567";
- //
- // get all users (optional here)
- v_EndpointUsers = v_BaseURL + "/restapi/portal/" + v_PortalID + "/projects/" + v_ProjectID + "/users/";
- r_Users = invokeUrl
- [
- url :v_EndpointUsers
- type :GET
- connection:"my_projects_connection"
- ];
- //
- // get all events
- // NB: status=open are upcoming events; status=closed are events in the past; status is a mandatory field
- // NB: range is an integer and the number of records to return (default=100; max=200)
- // NB: index is an integer and is the offset or starting index (not the ID of the record)
- m_Params = Map();
- m_Params.put("range",50);
- m_Params.put("status","closed");
- v_EndpointEvents = v_BaseURL + "/restapi/portal/" + v_PortalID + "/projects/" + v_ProjectID + "/events/";
- r_PastEvents = invokeUrl
- [
- url :v_EndpointEvents
- type :GET
- parameters:m_Params
- connection:"my_projects_connection"
- ];
- l_PastEvents = ifnull(r_PastEvents.get("events"),List());
- for each m_PastEvent in l_PastEvents
- {
- //
- // convert a Zoho Project DateTime to a DateTime datatype (given 12/12/2023 12:00:00 PM... remove ambiguity: mm/dd or dd/mm?)
- if(!isNull(m_PastEvent.get("scheduled_on")))
- {
- if(m_PastEvent.get("scheduled_on").contains(" "))
- {
- l_ThisDateTimeEventParts = m_PastEvent.get("scheduled_on").toList(" ");
- if(l_ThisDateTimeEventParts.get(0).contains("/"))
- {
- l_ThisDateEventParts = l_ThisDateTimeEventParts.get(0).toList("/");
- v_ThisDateEvent_Year = l_ThisDateEventParts.get(2);
- v_ThisDateEvent_Month = l_ThisDateEventParts.get(1);
- v_ThisDateEvent_Day = l_ThisDateEventParts.get(0);
- l_ThisTimeEventParts = l_ThisDateTimeEventParts.get(1).toList(":");
- v_ThisDateEvent_Minute = l_ThisTimeEventParts.get(1);
- v_ThisDateEvent_Hour = l_ThisTimeEventParts.get(0);
- //
- // convert 12-hour to 24-hour
- v_ThisDateEvent_Hour = if(l_ThisDateTimeEventParts.get(2)=="PM", v_ThisDateEvent_Hour.toLong() + 12, v_ThisDateEvent_Hour);
- v_ThisDateEvent_Hour = if(v_ThisDateEvent_Hour.toLong()==24, "12", v_ThisDateEvent_Hour);
- //
- v_ThisEventDate = v_ThisDateEvent_Year + "-" + v_ThisDateEvent_Month + "-" + v_ThisDateEvent_Day;
- v_ThisEventDateSort = v_ThisDateEvent_Year.toString() + v_ThisDateEvent_Month.toString() + v_ThisDateEvent_Day.toString() + v_ThisDateEvent_Hour.toString() + v_ThisDateEvent_Minute.toString() + ifnull(m_PastEvent.get("title"),"?").trim().toLowerCase();
- v_ThisEventStart = v_ThisDateEvent_Year + "-" + v_ThisDateEvent_Month + "-" + v_ThisDateEvent_Day + " " + v_ThisDateEvent_Hour + ":" + v_ThisDateEvent_Minute + ":00";
- //
- // date and time obtained... do your other stuff here
- }
- }
- }
- }
- //
- // get all future events
- // NB: status=open are upcoming events; status=closed are events in the past; status is a mandatory field
- // NB: range is an integer and the number of records to return (default=100; max=200)
- // NB: index is an integer and is the offset or starting index (not the ID of the record)
- m_Params = Map();
- m_Params.put("range",50);
- m_Params.put("status","open");
- v_EndpointEvents = v_BaseURL + "/restapi/portal/" + v_PortalID + "/projects/" + v_ProjectID + "/events/";
- r_FutureEvents = invokeUrl
- [
- url :v_EndpointEvents
- type :GET
- parameters:m_Params
- connection:"my_projects_connection"
- ];
- l_FutureEvents = ifnull(r_FutureEvents.get("events"),List());
Additional: Code to get a single event:
The above is great on a test system where you don't have many events, but what if this the system has far more than 50 events, past or upcoming? Here's the code to get a single event record by its record ID. Note that this isn't in the official documentation but at time of print, this works:
// // get event record by ID v_EndpointEvent = v_BaseURL + "/restapi/portal/" + v_PortalID + "/projects/" + v_ProjectID + "/events/" + v_EventID + "/"; r_ThisEvent = invokeurl [ url :v_EndpointEvent type :GET connection:"my_projects_connection" ]; info r_ThisEvent;
- //
- // get event record by ID
- v_EndpointEvent = v_BaseURL + "/restapi/portal/" + v_PortalID + "/projects/" + v_ProjectID + "/events/" + v_EventID + "/";
- r_ThisEvent = invokeUrl
- [
- url :v_EndpointEvent
- type :GET
- connection:"my_projects_connection"
- ];
- info r_ThisEvent;
Source(s):
- Zoho Projects - REST API - Documentation
- Zoho Projects - REST API - Documentation - Search for Events by Name