Beam Help
Get help now

How-to · Zoho CRM

How to retrieve signals in Zoho

Fetch signal data programmatically using Zoho's API.

Retrieving signals in Zoho requires a valid, active OAuth connection — the platform will reject any request with an expired token, so your integration must handle token refresh automatically before making API calls.


Why this matters


When you build automations or dashboards that pull live signals (activity data, record counts, status updates) from Zoho CRM or Zoho Desk, a stale access token will silently break your data pipeline. Understanding how the connection layer works lets you diagnose 401 errors quickly and keep your signal retrieval reliable. This is especially important in long-running sessions where tokens expire mid-stream. As independent expert support for Zoho (not official Zoho support), Beam Help walks you through the full flow below.


Step-by-step


Step 1. Verify a Zoho connection exists for the user.


Before attempting to retrieve any signals, confirm that a connection record is present in your data store. The lookup queries your connections table by user_id; if no record is found, the function returns None and no API call should proceed. [1]


Step 2. Check token expiry and refresh proactively.


Rather than waiting for a live 401 response, compare the current timestamp against the stored tokenexpiresat value. A recommended practice is to apply a 120-second skew — if the token will expire within the next two minutes, refresh it immediately before the signal retrieval call goes out. [1]


Step 3. Call ZohoOAuth.refresh_tokens with the stored refresh token.


Pass the refreshtoken from your connection record into the refresh method. Internally this posts a granttype: refreshtoken request along with your clientid and clientsecret to Zoho's token endpoint. On success you receive a new accesstoken and an updated tokenexpiresat. [6]


Step 4. Persist the new tokens immediately.


Write the refreshed accesstoken and tokenexpiresat back to your connections table before proceeding. This prevents a race condition where a parallel request uses the old, now-invalid token. Update the record using the userid as the key and also stamp an updated_at timestamp. [^1, ^5]


Step 5. Instantiate the correct API client for your signal type.


Use getzohoapi with the appropriate app_type — either "crm" for Zoho CRM signals or "desk" for Zoho Desk signals. This function internally calls the connection retrieval logic from Step 1–4 and returns both an api object and the raw conn dictionary. [5]


Step 6. Supply a token_refresher callback to the client.


When constructing ZohoCrmClient or ZohoDeskClient, pass a callable that re-runs the refresh flow. This allows the client to recover automatically if a token expires mid-request without requiring you to restart the entire retrieval. [^5, ^7]


Step 7. Execute the signal retrieval tool.


With a valid API instance in hand, call the relevant tool — for example getrecordcount to retrieve aggregate signals, or another named tool for specific record data. The execution wrapper accepts the api object, the app_type, the tool name, and a params dictionary. [3]


Step 8. Extract the result and build contextual links.


Once the tool returns, pull the payload from the toolresult key in the response. If your use case requires deep links back into the Zoho UI, pass the result through a link-builder along with the dc (data centre), crmorgid, deskorgid, and deskportal values from the connection record. [3]


Step 9. Handle the case where no connection is found.


If getzohoapi returns None for the api object, surface a clear message to the user — for example, prompting them to reconnect their Zoho account — rather than proceeding with a null client that will throw unhandled exceptions. [8]


Common pitfalls


  • Missing deskorgid for Zoho Desk signals. If the deskorgid field is blank in your connection record, the Desk client will attempt an auto-discovery call to fetch all organisations and persist the first result. This adds latency to the first signal retrieval. Pre-populate the org ID during the OAuth callback to avoid this. [5]
  • Token refresh returning an error key. The refresh response may contain an "error" key rather than an "accesstoken" key if the refresh token has been revoked. Always check for the absence of "accesstoken" — not just the presence of "error" — before deciding whether to update the stored token. [^6, ^4]
  • Race conditions in multi-user environments. If two requests for the same user fire simultaneously and both detect an expired token, both may attempt a refresh. The second refresh may invalidate the first new token. Serialise refresh calls per user or use a database-level lock. [1]

What to check


  • Confirm that tokenexpiresat in your connections table is being updated correctly after every successful refresh, and that the value is a Unix timestamp (integer), not an ISO string. [1]
  • Verify that the apptype passed to getzoho_api matches the signal source — using "crm" when querying Desk endpoints (or vice versa) will result in incorrect client initialisation. [5]
  • After retrieval, check that the tool_result key is present in the response before attempting to parse signal data; an absent key indicates the tool returned a clarifying question or an error rather than a data payload. [4]

Sources cited

  1. [1] server.py: get_zoho_connection
  2. [2] server.py: chat_plan_stream
  3. [3] server.py: chat_stream
  4. [4] run_api_tests.py
  5. [5] server.py: get_zoho_api
  6. [6] zoho_oauth.py
  7. [7] run_direct_tests.py
Retrieve Signals | Beam Help — Beam Help