In this step, your server makes a secure back-end request to Zoho's authorization server. Unlike Step 2, this is a POST request that happens entirely behind the scenes—no user interaction is required.
At Kalki LLP, we emphasize that this is a one-time exchange. The tokens received here should be stored securely in your database for all future API calls.
To exchange your Grant Token (code), send a POST request to the following endpoint based on your data center:
The request must be sent with the Content-Type: application/x-www-form-urlencoded.
| Parameter | Value | Description |
| code | {grant_token} | The 1000.xxxx code received in Step 2. |
| client_id | {your_client_id} | Obtained during Step 1 (Registration). |
| client_secret | {your_client_secret} | Obtained during Step 1. |
| redirect_uri | {your_redirect_uri} | Must match the URI used in Steps 1 & 2. |
| grant_type | authorization_code | Hardcoded value for this exchange. |
A successful request will return a JSON object containing the keys to your Zoho Books data:
{
"access_token": "1000.xxxx.yyyy",
"refresh_token": "1000.zzzz.aaaa",
"api_domain": "https://www.zohoapis.in",
"token_type": "Bearer",
"expires_in": 3600
}
access_token: Your temporary key to call the API. It is valid for 1 hour (3600 seconds).
refresh_token: Your permanent key. Use this to get a new access token once the current one expires. Note: This is only returned if you used access_type=offline in Step 2.
api_domain: The base URL you must use for all subsequent Books API calls (e.g., zohoapis.in).
This step is where most developers face "Invalid Code" errors. Use this table to debug:
| Error Log | Probable Cause | Fix for Kalki LLP Developers |
| invalid_code | Code expired or used twice. | Grant tokens expire in 1 minute. Ensure you exchange it immediately. |
| invalid_client | ID/Secret mismatch. | Verify you are using the correct Secret for the specific Data Center (.in vs .com). |
| invalid_redirect_uri | URI mismatch. | Ensure the redirect_uri is identical to what was registered in the console. |
| server_error | GET request used. | This endpoint only accepts POST requests. |
For kalkillp.com clients, we recommend the following security standards:
Encrypt the Refresh Token: Store it in an encrypted database field. If compromised, it gives permanent access to your books.
Handle the 20-Token Limit: Zoho allows only 20 active refresh tokens per user per app. If you generate a 21st, the 1st one becomes invalid.
Token Scoping: Never request more permissions than needed. If you only need to read invoices, use ZohoBooks.invoices.READ instead of fullaccess.
Tired of managing manual token refreshes and handling "Invalid Code" errors? Kalki LLP provides custom middleware and Deluge-based automation to handle the entire OAuth lifecycle for you.